Fix build
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositoriesView.java
blob9f9955e83c4ad6c88ff728bb2f36e03498504c9e
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.fetch.FetchConfiguredRemoteAction;
54 import org.eclipse.egit.ui.internal.fetch.FetchWizard;
55 import org.eclipse.egit.ui.internal.push.PushConfiguredRemoteAction;
56 import org.eclipse.egit.ui.internal.push.PushWizard;
57 import org.eclipse.egit.ui.internal.repository.tree.RepositoryNode;
58 import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
59 import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNodeType;
60 import org.eclipse.jface.action.Action;
61 import org.eclipse.jface.action.IAction;
62 import org.eclipse.jface.action.IToolBarManager;
63 import org.eclipse.jface.action.Separator;
64 import org.eclipse.jface.dialogs.MessageDialog;
65 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
66 import org.eclipse.jface.operation.IRunnableWithProgress;
67 import org.eclipse.jface.viewers.IOpenListener;
68 import org.eclipse.jface.viewers.ISelection;
69 import org.eclipse.jface.viewers.ISelectionChangedListener;
70 import org.eclipse.jface.viewers.ISelectionProvider;
71 import org.eclipse.jface.viewers.IStructuredSelection;
72 import org.eclipse.jface.viewers.ITreeContentProvider;
73 import org.eclipse.jface.viewers.OpenEvent;
74 import org.eclipse.jface.viewers.SelectionChangedEvent;
75 import org.eclipse.jface.viewers.StructuredSelection;
76 import org.eclipse.jface.viewers.TreeViewer;
77 import org.eclipse.jface.window.Window;
78 import org.eclipse.jface.wizard.Wizard;
79 import org.eclipse.jface.wizard.WizardDialog;
80 import org.eclipse.jgit.lib.Constants;
81 import org.eclipse.jgit.lib.IndexChangedEvent;
82 import org.eclipse.jgit.lib.Ref;
83 import org.eclipse.jgit.lib.RefUpdate;
84 import org.eclipse.jgit.lib.RefsChangedEvent;
85 import org.eclipse.jgit.lib.Repository;
86 import org.eclipse.jgit.lib.RepositoryCache;
87 import org.eclipse.jgit.lib.RepositoryConfig;
88 import org.eclipse.jgit.lib.RepositoryListener;
89 import org.eclipse.jgit.transport.RemoteConfig;
90 import org.eclipse.jgit.util.FS;
91 import org.eclipse.osgi.util.NLS;
92 import org.eclipse.swt.SWT;
93 import org.eclipse.swt.dnd.Clipboard;
94 import org.eclipse.swt.dnd.TextTransfer;
95 import org.eclipse.swt.dnd.Transfer;
96 import org.eclipse.swt.events.MenuDetectEvent;
97 import org.eclipse.swt.events.MenuDetectListener;
98 import org.eclipse.swt.events.SelectionAdapter;
99 import org.eclipse.swt.events.SelectionEvent;
100 import org.eclipse.swt.graphics.Point;
101 import org.eclipse.swt.widgets.Composite;
102 import org.eclipse.swt.widgets.Display;
103 import org.eclipse.swt.widgets.Menu;
104 import org.eclipse.swt.widgets.MenuItem;
105 import org.eclipse.swt.widgets.TreeItem;
106 import org.eclipse.ui.IEditorInput;
107 import org.eclipse.ui.IEditorPart;
108 import org.eclipse.ui.IFileEditorInput;
109 import org.eclipse.ui.IPageLayout;
110 import org.eclipse.ui.ISelectionListener;
111 import org.eclipse.ui.ISelectionService;
112 import org.eclipse.ui.IViewPart;
113 import org.eclipse.ui.IWorkbenchPart;
114 import org.eclipse.ui.PartInitException;
115 import org.eclipse.ui.PlatformUI;
116 import org.eclipse.ui.actions.ActionFactory;
117 import org.eclipse.ui.editors.text.EditorsUI;
118 import org.eclipse.ui.ide.FileStoreEditorInput;
119 import org.eclipse.ui.ide.IDE;
120 import org.eclipse.ui.part.IShowInTarget;
121 import org.eclipse.ui.part.ShowInContext;
122 import org.eclipse.ui.part.ViewPart;
123 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
124 import org.eclipse.ui.views.properties.IPropertySheetPage;
125 import org.eclipse.ui.views.properties.PropertySheet;
126 import org.eclipse.ui.views.properties.PropertySheetPage;
127 import org.osgi.service.prefs.BackingStoreException;
131 * The Git Repositories view.
132 * <p>
133 * This keeps track of a bunch of local directory names each of which represent
134 * a Git Repository. This list is stored in some Preferences object and used to
135 * build the tree in the view.
136 * <p>
137 * Implements {@link ISelectionProvider} in order to integrate with the
138 * Properties view.
139 * <p>
140 * This periodically refreshes itself in order to react on Repository changes.
142 public class RepositoriesView extends ViewPart implements ISelectionProvider,
143 IShowInTarget {
145 /** The view ID */
146 public static final String VIEW_ID = "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
148 // TODO central constants? RemoteConfig ones are private
149 static final String REMOTE = "remote"; //$NON-NLS-1$
151 static final String URL = "url"; //$NON-NLS-1$
153 static final String PUSHURL = "pushurl"; //$NON-NLS-1$
155 static final String FETCH = "fetch"; //$NON-NLS-1$
157 static final String PUSH = "push"; //$NON-NLS-1$
159 private static final String PREFS_DIRECTORIES = "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
161 private static final String PREFS_SYNCED = "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
163 private final Set<Repository> repositories = new HashSet<Repository>();
165 private final List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>();
167 private RepositoryListener repositoryListener;
169 private ISelection currentSelection = new StructuredSelection();
171 private Job scheduledJob;
173 private TreeViewer tv;
175 private IAction importAction;
177 private IAction addAction;
179 private IAction refreshAction;
181 private IAction linkWithSelectionAction;
183 private IAction copyAction;
185 private IAction pasteAction;
188 * TODO move to utility class
190 * @return the directories as configured for this view
192 public static List<String> getDirs() {
193 List<String> resultStrings = new ArrayList<String>();
194 String dirs = getPrefs().get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
195 if (dirs != null && dirs.length() > 0) {
196 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
197 while (tok.hasMoreTokens()) {
198 String dirName = tok.nextToken();
199 File testFile = new File(dirName);
200 if (testFile.exists() && !resultStrings.contains(dirName)) {
201 resultStrings.add(dirName);
205 Collections.sort(resultStrings);
206 return resultStrings;
209 private static void removeDir(File file) {
211 String dir;
212 try {
213 dir = file.getCanonicalPath();
214 } catch (IOException e1) {
215 dir = file.getAbsolutePath();
218 IEclipsePreferences prefs = getPrefs();
220 TreeSet<String> resultStrings = new TreeSet<String>();
221 String dirs = prefs.get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
222 if (dirs != null && dirs.length() > 0) {
223 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
224 while (tok.hasMoreTokens()) {
225 String dirName = tok.nextToken();
226 File testFile = new File(dirName);
227 if (testFile.exists()) {
228 try {
229 resultStrings.add(testFile.getCanonicalPath());
230 } catch (IOException e) {
231 resultStrings.add(testFile.getAbsolutePath());
237 if (resultStrings.remove(dir)) {
238 StringBuilder sb = new StringBuilder();
239 for (String gitDirString : resultStrings) {
240 sb.append(gitDirString);
241 sb.append(File.pathSeparatorChar);
244 prefs.put(PREFS_DIRECTORIES, sb.toString());
245 try {
246 prefs.flush();
247 } catch (BackingStoreException e) {
248 IStatus error = new Status(IStatus.ERROR, Activator
249 .getPluginId(), e.getMessage(), e);
250 Activator.getDefault().getLog().log(error);
256 @Override
257 public Object getAdapter(Class adapter) {
258 // integrate with Properties view
259 if (adapter == IPropertySheetPage.class) {
260 PropertySheetPage page = new PropertySheetPage();
261 page
262 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
263 page));
264 return page;
267 return super.getAdapter(adapter);
270 @Override
271 public void createPartControl(Composite parent) {
272 tv = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
273 tv.setContentProvider(new RepositoriesViewContentProvider());
274 // the label provider registers itself
275 new RepositoriesViewLabelProvider(tv);
277 getSite().setSelectionProvider(this);
279 tv.addSelectionChangedListener(new ISelectionChangedListener() {
281 public void selectionChanged(SelectionChangedEvent event) {
283 copyAction.setEnabled(false);
285 IStructuredSelection ssel = (IStructuredSelection) event
286 .getSelection();
287 if (ssel.size() == 1) {
288 RepositoryTreeNode node = (RepositoryTreeNode) ssel
289 .getFirstElement();
290 // allow copy on repository, file, or folder (copying the
291 // directory)
292 if (node.getType() == RepositoryTreeNodeType.REPO
293 || node.getType() == RepositoryTreeNodeType.WORKINGDIR
294 || node.getType() == RepositoryTreeNodeType.FOLDER
295 || node.getType() == RepositoryTreeNodeType.FILE) {
296 copyAction.setEnabled(true);
298 setSelection(new StructuredSelection(ssel.getFirstElement()));
299 } else {
300 setSelection(new StructuredSelection());
305 tv.addOpenListener(new IOpenListener() {
306 public void open(OpenEvent event) {
307 IStructuredSelection selection = (IStructuredSelection) event
308 .getSelection();
309 if (selection.isEmpty()) {
310 // nothing selected, ignore
311 return;
314 Object element = selection.getFirstElement();
315 ITreeContentProvider contentProvider = (ITreeContentProvider) tv
316 .getContentProvider();
317 if (contentProvider.hasChildren(element)) {
318 // this element has children, expand/collapse it
319 tv.setExpandedState(element, !tv.getExpandedState(element));
320 } else {
321 Object[] selectionArray = selection.toArray();
322 for (Object selectedElement : selectionArray) {
323 RepositoryTreeNode node = (RepositoryTreeNode) selectedElement;
324 // if any of the selected elements are not files, ignore
325 // the open request
326 if (node.getType() != RepositoryTreeNodeType.FILE
327 && node.getType() != RepositoryTreeNodeType.REF
328 && node.getType() != RepositoryTreeNodeType.TAG) {
329 return;
333 // open the files the user has selected
334 for (Object selectedElement : selectionArray) {
335 RepositoryTreeNode node = (RepositoryTreeNode) selectedElement;
336 if (node.getType() == RepositoryTreeNodeType.FILE)
337 openFile((File) node.getObject());
338 else if (node.getType() == RepositoryTreeNodeType.REF
339 || node.getType() == RepositoryTreeNodeType.TAG) {
340 Ref ref = (Ref) node.getObject();
341 if (!isBare(node.getRepository())
342 && ref.getName().startsWith(
343 Constants.R_REFS))
344 checkoutBranch(node, ref.getName());
351 createRepositoryChangedListener();
353 addContextMenu();
355 addActionsToToolbar();
357 scheduleRefresh();
359 ISelectionService srv = (ISelectionService) getSite().getService(
360 ISelectionService.class);
361 srv.addPostSelectionListener(new ISelectionListener() {
363 public void selectionChanged(IWorkbenchPart part,
364 ISelection selection) {
366 // if the "link with selection" toggle is off, we're done
367 if (linkWithSelectionAction == null
368 || !linkWithSelectionAction.isChecked())
369 return;
371 // this may happen if we switch between editors
372 if (part instanceof IEditorPart) {
373 IEditorInput input = ((IEditorPart) part).getEditorInput();
374 if (input instanceof IFileEditorInput)
375 reactOnSelection(new StructuredSelection(
376 ((IFileEditorInput) input).getFile()));
378 } else {
379 reactOnSelection(selection);
386 private void reactOnSelection(ISelection selection) {
387 if (selection instanceof StructuredSelection) {
388 StructuredSelection ssel = (StructuredSelection) selection;
389 if (ssel.size() != 1)
390 return;
391 if (ssel.getFirstElement() instanceof IResource) {
392 showResource((IResource) ssel.getFirstElement());
394 if (ssel.getFirstElement() instanceof IAdaptable) {
395 IResource adapted = (IResource) ((IAdaptable) ssel
396 .getFirstElement()).getAdapter(IResource.class);
397 if (adapted != null)
398 showResource(adapted);
403 private void addContextMenu() {
404 tv.getTree().addMenuDetectListener(new MenuDetectListener() {
406 public void menuDetected(MenuDetectEvent e) {
407 Menu men = tv.getTree().getMenu();
408 if (men != null) {
409 men.dispose();
411 men = new Menu(tv.getTree());
413 TreeItem testItem = tv.getTree().getItem(
414 tv.getTree().toControl(new Point(e.x, e.y)));
415 if (testItem == null) {
416 addMenuItemsForPanel(men);
417 } else {
418 addMenuItemsForTreeSelection(men);
421 tv.getTree().setMenu(men);
426 private void addMenuItemsForPanel(Menu men) {
428 MenuItem importItem = new MenuItem(men, SWT.PUSH);
429 importItem.setText(UIText.RepositoriesView_ImportRepository_MenuItem);
430 importItem.addSelectionListener(new SelectionAdapter() {
432 @Override
433 public void widgetSelected(SelectionEvent e) {
434 importAction.run();
439 MenuItem addItem = new MenuItem(men, SWT.PUSH);
440 addItem.setText(UIText.RepositoriesView_AddRepository_MenuItem);
441 addItem.addSelectionListener(new SelectionAdapter() {
443 @Override
444 public void widgetSelected(SelectionEvent e) {
445 addAction.run();
450 MenuItem pasteItem = new MenuItem(men, SWT.PUSH);
451 pasteItem.setText(UIText.RepositoriesView_PasteMenu);
452 pasteItem.addSelectionListener(new SelectionAdapter() {
454 @Override
455 public void widgetSelected(SelectionEvent e) {
456 pasteAction.run();
461 MenuItem refreshItem = new MenuItem(men, SWT.PUSH);
462 refreshItem.setText(refreshAction.getText());
463 refreshItem.addSelectionListener(new SelectionAdapter() {
465 @Override
466 public void widgetSelected(SelectionEvent e) {
467 refreshAction.run();
474 private void addMenuItemsForTreeSelection(Menu men) {
476 final IStructuredSelection sel = (IStructuredSelection) tv
477 .getSelection();
479 boolean repoOnly = true;
480 for (Object selected : sel.toArray()) {
482 if (((RepositoryTreeNode) selected).getType() != RepositoryTreeNodeType.REPO) {
483 repoOnly = false;
484 break;
488 if (sel.size() > 1 && repoOnly) {
489 List nodes = sel.toList();
490 final Repository[] repos = new Repository[nodes.size()];
491 for (int i = 0; i < sel.size(); i++)
492 repos[i] = ((RepositoryTreeNode) nodes.get(i)).getRepository();
494 MenuItem remove = new MenuItem(men, SWT.PUSH);
495 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
496 remove.addSelectionListener(new SelectionAdapter() {
498 @Override
499 public void widgetSelected(SelectionEvent e) {
500 // TODO progress monitoring/cancellation
501 removeRepository(new NullProgressMonitor(), repos);
507 // from here on, we only deal with single selection
508 if (sel.size() > 1)
509 return;
511 final RepositoryTreeNode node = (RepositoryTreeNode) sel
512 .getFirstElement();
514 final boolean isBare = isBare(node.getRepository());
516 if (node.getType() == RepositoryTreeNodeType.REF) {
518 final Ref ref = (Ref) node.getObject();
520 // we don't check out symbolic references
521 if (!ref.isSymbolic()) {
523 if (!isBare) {
524 MenuItem checkout = new MenuItem(men, SWT.PUSH);
525 checkout.setText(UIText.RepositoriesView_CheckOut_MenuItem);
527 checkout.setEnabled(!isRefCheckedOut(node.getRepository(),
528 ref.getName()));
530 checkout.addSelectionListener(new SelectionAdapter() {
532 @Override
533 public void widgetSelected(SelectionEvent e) {
534 checkoutBranch(node, ref.getLeaf().getName());
538 new MenuItem(men, SWT.SEPARATOR);
541 createCreateBranchItem(men, node);
542 createDeleteBranchItem(men, node);
547 if (node.getType() == RepositoryTreeNodeType.TAG) {
549 final Ref ref = (Ref) node.getObject();
551 MenuItem checkout = new MenuItem(men, SWT.PUSH);
552 checkout.setText(UIText.RepositoriesView_CheckOut_MenuItem);
554 checkout.setEnabled(!isRefCheckedOut(node.getRepository(), ref
555 .getName()));
557 checkout.addSelectionListener(new SelectionAdapter() {
559 @Override
560 public void widgetSelected(SelectionEvent e) {
561 checkoutBranch(node, ref.getLeaf().getName());
566 if (node.getType() == RepositoryTreeNodeType.BRANCHES
567 || node.getType() == RepositoryTreeNodeType.LOCALBRANCHES)
568 // offering this on the "Remote Branches" node would probably be
569 // confusing
570 createCreateBranchItem(men, node);
572 // for Repository: import existing projects, remove, (delete), open
573 // properties
574 if (node.getType() == RepositoryTreeNodeType.REPO) {
576 final Repository repo = (Repository) node.getObject();
578 // TODO "import existing plug-in" menu item
580 MenuItem remove = new MenuItem(men, SWT.PUSH);
581 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
582 remove.addSelectionListener(new SelectionAdapter() {
584 @Override
585 public void widgetSelected(SelectionEvent e) {
586 // TODO progress monitoring/cancellation
587 removeRepository(new NullProgressMonitor(), repo);
591 new MenuItem(men, SWT.SEPARATOR);
593 MenuItem fetchItem = new MenuItem(men, SWT.PUSH);
594 fetchItem.setText(UIText.RepositoriesView_FetchMenu);
595 fetchItem.setImage(UIIcons.FETCH.createImage());
596 fetchItem.addSelectionListener(new SelectionAdapter() {
598 @Override
599 public void widgetSelected(SelectionEvent e) {
600 try {
601 new WizardDialog(getSite().getShell(), new FetchWizard(
602 repo)).open();
603 } catch (URISyntaxException e1) {
604 Activator.handleError(e1.getMessage(), e1, true);
610 MenuItem pushItem = new MenuItem(men, SWT.PUSH);
611 pushItem.setText(UIText.RepositoriesView_PushMenu);
612 pushItem.setImage(UIIcons.PUSH.createImage());
613 pushItem.addSelectionListener(new SelectionAdapter() {
615 @Override
616 public void widgetSelected(SelectionEvent e) {
617 try {
618 new WizardDialog(getSite().getShell(), new PushWizard(
619 repo)).open();
620 } catch (URISyntaxException e1) {
621 Activator.handleError(e1.getMessage(), e1, true);
627 // TODO delete does not work because of file locks on .pack-files
628 // Shawn Pearce has added the following thoughts:
630 // Hmm. We probably can't active detect file locks on pack files on
631 // Windows, can we?
632 // It would be nice if we could support a delete, but only if the
633 // repository is
634 // reasonably believed to be not-in-use right now.
636 // Within EGit you might be able to check GitProjectData and its
637 // repositoryCache to
638 // see if the repository is open by this workspace. If it is, then
639 // we know we shouldn't
640 // try to delete it.
642 // Some coding might look like this:
644 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
645 // deleteRepo.setText("Delete");
646 // deleteRepo.addSelectionListener(new SelectionAdapter() {
648 // @Override
649 // public void widgetSelected(SelectionEvent e) {
651 // boolean confirmed = MessageDialog.openConfirm(getSite()
652 // .getShell(), "Confirm",
653 // "This will delete the repository, continue?");
655 // if (!confirmed)
656 // return;
658 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
660 // public void run(IProgressMonitor monitor)
661 // throws CoreException {
662 // File workDir = repos.get(0).getRepository()
663 // .getWorkDir();
665 // File gitDir = repos.get(0).getRepository()
666 // .getDirectory();
668 // IPath wdPath = new Path(workDir.getAbsolutePath());
669 // for (IProject prj : ResourcesPlugin.getWorkspace()
670 // .getRoot().getProjects()) {
671 // if (wdPath.isPrefixOf(prj.getLocation())) {
672 // prj.delete(false, false, monitor);
673 // }
674 // }
676 // repos.get(0).getRepository().close();
678 // boolean deleted = deleteRecursively(gitDir, monitor);
679 // if (!deleted) {
680 // MessageDialog.openError(getSite().getShell(),
681 // "Error",
682 // "Could not delete Git Repository");
683 // }
685 // deleted = deleteRecursively(workDir, monitor);
686 // if (!deleted) {
687 // MessageDialog
688 // .openError(getSite().getShell(),
689 // "Error",
690 // "Could not delete Git Working Directory");
691 // }
693 // scheduleRefresh();
694 // }
696 // private boolean deleteRecursively(File fileToDelete,
697 // IProgressMonitor monitor) {
698 // if (fileToDelete.isDirectory()) {
699 // for (File file : fileToDelete.listFiles()) {
700 // if (!deleteRecursively(file, monitor)) {
701 // return false;
702 // }
703 // }
704 // }
705 // monitor.setTaskName(fileToDelete.getAbsolutePath());
706 // boolean deleted = fileToDelete.delete();
707 // if (!deleted) {
708 // System.err.println("Could not delete "
709 // + fileToDelete.getAbsolutePath());
710 // }
711 // return deleted;
712 // }
713 // };
715 // try {
716 // ResourcesPlugin.getWorkspace().run(wsr,
717 // ResourcesPlugin.getWorkspace().getRoot(),
718 // IWorkspace.AVOID_UPDATE,
719 // new NullProgressMonitor());
720 // } catch (CoreException e1) {
721 // // TODO Exception handling
722 // e1.printStackTrace();
723 // }
725 // }
727 // });
729 new MenuItem(men, SWT.SEPARATOR);
731 if (!isBare) {
732 createImportProjectItem(men, repo, repo.getWorkDir().getPath());
734 new MenuItem(men, SWT.SEPARATOR);
737 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
738 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
739 openPropsView.addSelectionListener(new SelectionAdapter() {
741 @Override
742 public void widgetSelected(SelectionEvent e) {
743 try {
744 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
745 .getActivePage().showView(
746 IPageLayout.ID_PROP_SHEET);
747 } catch (PartInitException e1) {
748 // just ignore
754 new MenuItem(men, SWT.SEPARATOR);
756 createCopyPathItem(men, repo.getDirectory().getPath());
759 if (node.getType() == RepositoryTreeNodeType.REMOTES) {
761 MenuItem remoteConfig = new MenuItem(men, SWT.PUSH);
762 remoteConfig.setText(UIText.RepositoriesView_NewRemoteMenu);
763 remoteConfig.addSelectionListener(new SelectionAdapter() {
765 @Override
766 public void widgetSelected(SelectionEvent e) {
768 WizardDialog dlg = new WizardDialog(getSite().getShell(),
769 new NewRemoteWizard(node.getRepository()));
770 if (dlg.open() == Window.OK)
771 tv.refresh();
778 if (node.getType() == RepositoryTreeNodeType.REMOTE) {
780 final String configName = (String) node.getObject();
782 RemoteConfig rconfig;
783 try {
784 rconfig = new RemoteConfig(node.getRepository().getConfig(),
785 configName);
786 } catch (URISyntaxException e2) {
787 // TODO Exception handling
788 rconfig = null;
791 boolean fetchExists = rconfig != null
792 && !rconfig.getURIs().isEmpty();
793 boolean pushExists = rconfig != null
794 && !rconfig.getPushURIs().isEmpty();
796 if (!fetchExists) {
797 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
798 configureUrlFetch
799 .setText(UIText.RepositoriesView_CreateFetch_menu);
801 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
803 @Override
804 public void widgetSelected(SelectionEvent e) {
806 WizardDialog dlg = new WizardDialog(getSite()
807 .getShell(), new ConfigureRemoteWizard(node
808 .getRepository(), configName, false));
809 if (dlg.open() == Window.OK)
810 tv.refresh();
817 if (!pushExists) {
818 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
820 configureUrlPush
821 .setText(UIText.RepositoriesView_CreatePush_menu);
823 configureUrlPush.addSelectionListener(new SelectionAdapter() {
825 @Override
826 public void widgetSelected(SelectionEvent e) {
828 WizardDialog dlg = new WizardDialog(getSite()
829 .getShell(), new ConfigureRemoteWizard(node
830 .getRepository(), configName, true));
831 if (dlg.open() == Window.OK)
832 tv.refresh();
839 if (!fetchExists || !pushExists)
840 // add a separator dynamically
841 new MenuItem(men, SWT.SEPARATOR);
843 MenuItem removeRemote = new MenuItem(men, SWT.PUSH);
844 removeRemote.setText(UIText.RepositoriesView_RemoveRemoteMenu);
845 removeRemote.addSelectionListener(new SelectionAdapter() {
847 @Override
848 public void widgetSelected(SelectionEvent e) {
850 boolean ok = MessageDialog
851 .openConfirm(
852 getSite().getShell(),
853 UIText.RepositoriesView_ConfirmDeleteRemoteHeader,
855 .bind(
856 UIText.RepositoriesView_ConfirmDeleteRemoteMessage,
857 configName));
858 if (ok) {
859 RepositoryConfig config = node.getRepository()
860 .getConfig();
861 config.unsetSection(REMOTE, configName);
862 try {
863 config.save();
864 tv.refresh();
865 } catch (IOException e1) {
866 Activator.handleError(
867 UIText.RepositoriesView_ErrorHeader, e1,
868 true);
876 new MenuItem(men, SWT.SEPARATOR);
878 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
879 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
880 openPropsView.addSelectionListener(new SelectionAdapter() {
882 @Override
883 public void widgetSelected(SelectionEvent e) {
884 try {
885 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
886 .getActivePage().showView(
887 IPageLayout.ID_PROP_SHEET);
888 } catch (PartInitException e1) {
889 // just ignore
896 if (node.getType() == RepositoryTreeNodeType.FETCH) {
898 final String configName = (String) node.getParent().getObject();
900 MenuItem doFetch = new MenuItem(men, SWT.PUSH);
901 doFetch.setText(UIText.RepositoriesView_DoFetchMenu);
902 doFetch.addSelectionListener(new SelectionAdapter() {
904 @Override
905 public void widgetSelected(SelectionEvent evt) {
906 new FetchConfiguredRemoteAction(node.getRepository(),
907 configName).run(getSite().getShell());
912 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
913 configureUrlFetch
914 .setText(UIText.RepositoriesView_ConfigureFetchMenu);
916 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
918 @Override
919 public void widgetSelected(SelectionEvent e) {
921 WizardDialog dlg = new WizardDialog(getSite().getShell(),
922 new ConfigureRemoteWizard(node.getRepository(),
923 configName, false));
924 if (dlg.open() == Window.OK)
925 tv.refresh();
931 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
932 deleteFetch.setText(UIText.RepositoriesView_RemoveFetch_menu);
933 deleteFetch.addSelectionListener(new SelectionAdapter() {
935 @Override
936 public void widgetSelected(SelectionEvent e) {
937 RepositoryConfig config = node.getRepository().getConfig();
938 config.unset("remote", configName, "url"); //$NON-NLS-1$ //$NON-NLS-2$
939 config.unset("remote", configName, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
940 try {
941 config.save();
942 tv.refresh();
943 } catch (IOException e1) {
944 MessageDialog.openError(getSite().getShell(),
945 UIText.RepositoriesView_ErrorHeader, e1
946 .getMessage());
954 if (node.getType() == RepositoryTreeNodeType.PUSH) {
956 final String configName = (String) node.getParent().getObject();
958 MenuItem doPush = new MenuItem(men, SWT.PUSH);
959 doPush.setText(UIText.RepositoriesView_DoPushMenuItem);
960 doPush.addSelectionListener(new SelectionAdapter() {
962 @Override
963 public void widgetSelected(SelectionEvent evt) {
964 new PushConfiguredRemoteAction(node.getRepository(),
965 configName).run(getSite().getShell(), false);
969 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
970 configureUrlPush.setText(UIText.RepositoriesView_ConfigurePushMenu);
972 configureUrlPush.addSelectionListener(new SelectionAdapter() {
974 @Override
975 public void widgetSelected(SelectionEvent e) {
977 WizardDialog dlg = new WizardDialog(getSite().getShell(),
978 new ConfigureRemoteWizard(node.getRepository(),
979 configName, true));
980 if (dlg.open() == Window.OK)
981 tv.refresh();
986 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
987 deleteFetch.setText(UIText.RepositoriesView_RemovePush_menu);
988 deleteFetch.addSelectionListener(new SelectionAdapter() {
990 @Override
991 public void widgetSelected(SelectionEvent e) {
992 RepositoryConfig config = node.getRepository().getConfig();
993 config.unset("remote", configName, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
994 config.unset("remote", configName, "push"); //$NON-NLS-1$ //$NON-NLS-2$
995 try {
996 config.save();
997 tv.refresh();
998 } catch (IOException e1) {
999 MessageDialog.openError(getSite().getShell(),
1000 UIText.RepositoriesView_ErrorHeader, e1
1001 .getMessage());
1008 if (node.getType() == RepositoryTreeNodeType.FILE) {
1010 final File file = (File) node.getObject();
1012 MenuItem openInTextEditor = new MenuItem(men, SWT.PUSH);
1013 openInTextEditor
1014 .setText(UIText.RepositoriesView_OpenInTextEditor_menu);
1015 openInTextEditor.addSelectionListener(new SelectionAdapter() {
1017 @Override
1018 public void widgetSelected(SelectionEvent e) {
1019 openFile(file);
1024 new MenuItem(men, SWT.SEPARATOR);
1025 createCopyPathItem(men, file.getPath());
1028 if (!isBare && node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1029 String path = node.getRepository().getWorkDir().getAbsolutePath();
1030 createImportProjectItem(men, node.getRepository(), path);
1031 new MenuItem(men, SWT.SEPARATOR);
1032 createCopyPathItem(men, path);
1035 if (node.getType() == RepositoryTreeNodeType.FOLDER) {
1036 String path = ((File) node.getObject()).getPath();
1037 createImportProjectItem(men, node.getRepository(), path);
1038 new MenuItem(men, SWT.SEPARATOR);
1039 createCopyPathItem(men, path);
1044 private boolean isBare(Repository repository) {
1045 return repository.getConfig().getBoolean("core", "bare", false); //$NON-NLS-1$ //$NON-NLS-2$
1048 private boolean isRefCheckedOut(Repository repository, String refName) {
1049 String branchName;
1050 String compareString;
1052 try {
1053 branchName = repository.getFullBranch();
1054 if (branchName == null)
1055 return false;
1056 if (refName.startsWith(Constants.R_HEADS)) {
1057 // local branch: HEAD would be on the branch
1058 compareString = refName;
1059 } else if (refName.startsWith(Constants.R_TAGS)) {
1060 // tag: HEAD would be on the commit id to which the tag is
1061 // pointing
1062 compareString = repository.mapTag(refName).getObjId().getName();
1063 } else if (refName.startsWith(Constants.R_REMOTES)) {
1064 // remote branch: HEAD would be on the commit id to which
1065 // the branch is pointing
1066 compareString = repository.mapCommit(refName).getCommitId()
1067 .getName();
1068 } else {
1069 // some other symbolic reference
1070 return false;
1072 } catch (IOException e1) {
1073 return false;
1076 return compareString.equals(branchName);
1079 private void createCopyPathItem(Menu men, final String path) {
1081 MenuItem copyPath;
1082 copyPath = new MenuItem(men, SWT.PUSH);
1083 copyPath.setText(UIText.RepositoriesView_CopyPathToClipboardMenu);
1084 copyPath.addSelectionListener(new SelectionAdapter() {
1086 @Override
1087 public void widgetSelected(SelectionEvent e) {
1088 Clipboard clipboard = new Clipboard(null);
1089 TextTransfer textTransfer = TextTransfer.getInstance();
1090 Transfer[] transfers = new Transfer[] { textTransfer };
1091 Object[] data = new Object[] { path };
1092 clipboard.setContents(data, transfers);
1093 clipboard.dispose();
1100 private void createCreateBranchItem(Menu men, final RepositoryTreeNode node) {
1101 final Ref ref;
1102 if (node.getType() == RepositoryTreeNodeType.REF)
1103 ref = (Ref) node.getObject();
1104 else
1105 ref = null;
1107 MenuItem createLocal = new MenuItem(men, SWT.PUSH);
1108 createLocal.setText(UIText.RepositoriesView_NewBranchMenu);
1110 createLocal.addSelectionListener(new SelectionAdapter() {
1112 @Override
1113 public void widgetSelected(SelectionEvent e) {
1115 Wizard wiz = new Wizard() {
1117 @Override
1118 public void addPages() {
1119 addPage(new CreateBranchPage(node.getRepository(), ref));
1120 setWindowTitle(UIText.RepositoriesView_NewBranchTitle);
1123 @Override
1124 public boolean performFinish() {
1126 try {
1127 getContainer().run(false, true,
1128 new IRunnableWithProgress() {
1130 public void run(IProgressMonitor monitor)
1131 throws InvocationTargetException,
1132 InterruptedException {
1133 CreateBranchPage cp = (CreateBranchPage) getPages()[0];
1134 try {
1135 cp.createBranch(monitor);
1136 } catch (CoreException ce) {
1137 throw new InvocationTargetException(
1138 ce);
1139 } catch (IOException ioe) {
1140 throw new InvocationTargetException(
1141 ioe);
1146 } catch (InvocationTargetException ite) {
1147 Activator
1148 .handleError(
1149 UIText.RepositoriesView_BranchCreationFailureMessage,
1150 ite.getCause(), true);
1151 return false;
1152 } catch (InterruptedException ie) {
1153 // ignore here
1155 return true;
1158 if (new WizardDialog(getSite().getShell(), wiz).open() == Window.OK)
1159 tv.refresh();
1165 private void createDeleteBranchItem(Menu men, final RepositoryTreeNode node) {
1167 final Ref ref = (Ref) node.getObject();
1169 MenuItem deleteBranch = new MenuItem(men, SWT.PUSH);
1170 deleteBranch.setText(UIText.RepositoriesView_DeleteBranchMenu);
1172 deleteBranch.setEnabled(!isRefCheckedOut(node.getRepository(), ref
1173 .getName()));
1175 deleteBranch.addSelectionListener(new SelectionAdapter() {
1177 @Override
1178 public void widgetSelected(SelectionEvent e) {
1180 if (!MessageDialog
1181 .openConfirm(
1182 getSite().getShell(),
1183 UIText.RepositoriesView_ConfirmDeleteTitle,
1185 .bind(
1186 UIText.RepositoriesView_ConfirmBranchDeletionMessage,
1187 ref.getName())))
1188 return;
1190 try {
1191 new ProgressMonitorDialog(getSite().getShell()).run(false,
1192 false, new IRunnableWithProgress() {
1194 public void run(IProgressMonitor monitor)
1195 throws InvocationTargetException,
1196 InterruptedException {
1198 try {
1199 RefUpdate op = node.getRepository()
1200 .updateRef(ref.getName());
1201 op.setRefLogMessage("branch deleted", //$NON-NLS-1$
1202 false);
1203 // we set the force update in order
1204 // to avoid having this rejected
1205 // due to minor issues
1206 op.setForceUpdate(true);
1207 op.delete();
1208 tv.refresh();
1209 } catch (IOException ioe) {
1210 throw new InvocationTargetException(ioe);
1215 } catch (InvocationTargetException e1) {
1216 Activator
1217 .handleError(
1218 UIText.RepositoriesView_BranchDeletionFailureMessage,
1219 e1.getCause(), true);
1220 e1.printStackTrace();
1221 } catch (InterruptedException e1) {
1222 // ignore
1230 private void openFile(File file) {
1231 IFileStore store = EFS.getLocalFileSystem().getStore(
1232 new Path(file.getAbsolutePath()));
1233 try {
1234 // TODO do we need a read-only editor here?
1235 IDE.openEditor(getSite().getPage(),
1236 new FileStoreEditorInput(store),
1237 EditorsUI.DEFAULT_TEXT_EDITOR_ID);
1238 } catch (PartInitException e) {
1239 Activator.handleError(UIText.RepositoriesView_Error_WindowTitle, e,
1240 true);
1244 private void checkoutBranch(final RepositoryTreeNode node,
1245 final String refName) {
1246 // for the sake of UI responsiveness, let's start a job
1247 Job job = new Job(NLS.bind(UIText.RepositoriesView_CheckingOutMessage,
1248 refName)) {
1250 @Override
1251 protected IStatus run(IProgressMonitor monitor) {
1253 Repository repo = node.getRepository();
1255 final BranchOperation op = new BranchOperation(repo, refName);
1256 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
1258 public void run(IProgressMonitor myMonitor)
1259 throws CoreException {
1260 op.execute(myMonitor);
1264 try {
1265 ResourcesPlugin.getWorkspace().run(wsr,
1266 ResourcesPlugin.getWorkspace().getRoot(),
1267 IWorkspace.AVOID_UPDATE, monitor);
1268 Display.getDefault().syncExec(new Runnable() {
1270 public void run() {
1271 tv.refresh();
1275 } catch (CoreException e1) {
1276 return new Status(IStatus.ERROR, Activator.getPluginId(),
1277 e1.getMessage(), e1);
1280 return Status.OK_STATUS;
1284 job.setUser(true);
1285 job.schedule();
1288 private void createImportProjectItem(Menu men, final Repository repo,
1289 final String path) {
1291 MenuItem startWizard;
1292 startWizard = new MenuItem(men, SWT.PUSH);
1293 startWizard.setText(UIText.RepositoriesView_ImportProjectsMenu);
1294 startWizard.addSelectionListener(new SelectionAdapter() {
1296 @Override
1297 public void widgetSelected(SelectionEvent e) {
1298 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1299 new GitCreateProjectViaWizardWizard(repo, path));
1300 dlg.open();
1305 // we could start the ImportWizard here,
1306 // unfortunately, this fails within a wizard
1307 // startWizard = new MenuItem(men, SWT.PUSH);
1308 // startWizard.setText("Start the Import wizard...");
1309 // startWizard.addSelectionListener(new SelectionAdapter() {
1311 // @Override
1312 // public void widgetSelected(SelectionEvent e) {
1314 // IHandlerService handlerService = (IHandlerService) getSite()
1315 // .getWorkbenchWindow().getWorkbench().getService(
1316 // IHandlerService.class);
1318 // try {
1319 // handlerService.executeCommand("org.eclipse.ui.file.import", //$NON-NLS-1$
1320 // null);
1321 // } catch (ExecutionException e1) {
1322 // Activator.handleError(e1.getMessage(), e1, true);
1323 // } catch (NotDefinedException e1) {
1324 // Activator.handleError(e1.getMessage(), e1, true);
1325 // } catch (NotEnabledException e1) {
1326 // Activator.handleError(e1.getMessage(), e1, true);
1327 // } catch (NotHandledException e1) {
1328 // Activator.handleError(e1.getMessage(), e1, true);
1329 // }
1330 // }
1332 // });
1335 private void addActionsToToolbar() {
1337 IToolBarManager manager = getViewSite().getActionBars()
1338 .getToolBarManager();
1340 refreshAction = new Action(UIText.RepositoriesView_Refresh_Button) {
1342 @Override
1343 public void run() {
1344 scheduleRefresh();
1347 refreshAction.setImageDescriptor(UIIcons.ELCL16_REFRESH);
1348 manager.add(refreshAction);
1350 linkWithSelectionAction = new Action(
1351 UIText.RepositoriesView_LinkWithSelection_action,
1352 IAction.AS_CHECK_BOX) {
1354 @Override
1355 public void run() {
1356 IEclipsePreferences prefs = getPrefs();
1357 prefs.putBoolean(PREFS_SYNCED, isChecked());
1358 try {
1359 prefs.flush();
1360 } catch (BackingStoreException e) {
1361 // ignore here
1363 if (isChecked()) {
1364 ISelectionService srv = (ISelectionService) getSite()
1365 .getService(ISelectionService.class);
1366 reactOnSelection(srv.getSelection());
1373 linkWithSelectionAction
1374 .setToolTipText(UIText.RepositoriesView_LinkWithSelection_action);
1375 linkWithSelectionAction.setImageDescriptor(UIIcons.ELCL16_SYNCED);
1376 linkWithSelectionAction.setChecked(getPrefs().getBoolean(PREFS_SYNCED,
1377 false));
1379 manager.add(linkWithSelectionAction);
1381 manager.add(new Separator());
1383 IAction collapseAllAction = new Action(
1384 UIText.RepositoriesView_CollapseAllMenu) {
1386 @Override
1387 public void run() {
1388 tv.collapseAll();
1391 collapseAllAction.setImageDescriptor(UIIcons.COLLAPSEALL);
1392 manager.add(collapseAllAction);
1394 manager.add(new Separator());
1396 importAction = new Action(UIText.RepositoriesView_Import_Button) {
1398 @Override
1399 public void run() {
1400 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1401 new GitCloneWizard());
1402 if (dlg.open() == Window.OK)
1403 scheduleRefresh();
1406 importAction.setToolTipText(UIText.RepositoriesView_Clone_Tooltip);
1407 importAction.setImageDescriptor(UIIcons.CLONEGIT);
1409 manager.add(importAction);
1411 addAction = new Action(UIText.RepositoriesView_Add_Button) {
1413 @Override
1414 public void run() {
1415 RepositorySearchDialog sd = new RepositorySearchDialog(
1416 getSite().getShell(), getDirs());
1417 if (sd.open() == Window.OK) {
1418 Set<String> dirs = new HashSet<String>();
1419 dirs.addAll(getDirs());
1420 if (dirs.addAll(sd.getDirectories()))
1421 saveDirs(dirs);
1422 scheduleRefresh();
1427 addAction.setToolTipText(UIText.RepositoriesView_AddRepository_Tooltip);
1428 addAction.setImageDescriptor(UIIcons.NEW_REPOSITORY);
1430 manager.add(addAction);
1432 // copy and paste are global actions; we just implement them
1433 // and register them with the global action handler
1434 // we enable/disable them upon tree selection changes
1436 copyAction = new Action("") { //$NON-NLS-1$
1438 @Override
1439 public void run() {
1440 // for REPO, WORKINGDIR, FILE, FOLDER: copy directory
1441 IStructuredSelection sel = (IStructuredSelection) tv
1442 .getSelection();
1443 if (sel.size() == 1) {
1444 RepositoryTreeNode node = (RepositoryTreeNode) sel
1445 .getFirstElement();
1446 String dir = null;
1447 if (node.getType() == RepositoryTreeNodeType.REPO) {
1448 dir = node.getRepository().getDirectory().getPath();
1449 } else if (node.getType() == RepositoryTreeNodeType.FILE
1450 || node.getType() == RepositoryTreeNodeType.FOLDER) {
1451 dir = ((File) node.getObject()).getPath();
1452 } else if (node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1453 if (!isBare(node.getRepository()))
1454 dir = node.getRepository().getWorkDir().getPath();
1456 if (dir != null) {
1457 Clipboard clip = null;
1458 try {
1459 clip = new Clipboard(getSite().getShell()
1460 .getDisplay());
1461 clip
1462 .setContents(new Object[] { dir },
1463 new Transfer[] { TextTransfer
1464 .getInstance() });
1465 } finally {
1466 if (clip != null)
1467 // we must dispose ourselves
1468 clip.dispose();
1475 copyAction.setEnabled(false);
1477 getViewSite().getActionBars().setGlobalActionHandler(
1478 ActionFactory.COPY.getId(), copyAction);
1480 pasteAction = new Action("") { //$NON-NLS-1$
1482 @Override
1483 public void run() {
1484 // we check if the pasted content is a directory
1485 // repository location and try to add this
1486 String errorMessage = null;
1488 Clipboard clip = null;
1489 try {
1490 clip = new Clipboard(getSite().getShell().getDisplay());
1491 String content = (String) clip.getContents(TextTransfer
1492 .getInstance());
1493 if (content == null) {
1494 errorMessage = UIText.RepositoriesView_NothingToPasteMessage;
1495 return;
1498 File file = new File(content);
1499 if (!file.exists() || !file.isDirectory()) {
1500 errorMessage = UIText.RepositoriesView_ClipboardContentNotDirectoryMessage;
1501 return;
1504 if (!RepositoryCache.FileKey.isGitRepository(file, FS.DETECTED)) {
1505 errorMessage = NLS
1506 .bind(
1507 UIText.RepositoriesView_ClipboardContentNoGitRepoMessage,
1508 content);
1509 return;
1512 if (addDir(file))
1513 scheduleRefresh();
1514 else
1515 errorMessage = NLS.bind(
1516 UIText.RepositoriesView_PasteRepoAlreadyThere,
1517 content);
1518 } finally {
1519 if (clip != null)
1520 // we must dispose ourselves
1521 clip.dispose();
1522 if (errorMessage != null)
1523 MessageDialog.openWarning(getSite().getShell(),
1524 UIText.RepositoriesView_PasteFailureTitle,
1525 errorMessage);
1531 getViewSite().getActionBars().setGlobalActionHandler(
1532 ActionFactory.PASTE.getId(), pasteAction);
1537 * @return the preferences
1539 protected static IEclipsePreferences getPrefs() {
1540 return new InstanceScope().getNode(Activator.getPluginId());
1543 @Override
1544 public void dispose() {
1545 // make sure to cancel the refresh job
1546 if (this.scheduledJob != null) {
1547 this.scheduledJob.cancel();
1548 this.scheduledJob = null;
1550 // remove RepositoryChangedListener
1551 unregisterRepositoryListener();
1552 repositories.clear();
1553 super.dispose();
1557 * Schedules a refresh
1559 private void scheduleRefresh() {
1561 if (scheduledJob != null && scheduledJob.getState() == Job.RUNNING)
1562 return;
1564 Job job = new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1566 @SuppressWarnings("unchecked")
1567 @Override
1568 protected IStatus run(IProgressMonitor monitor) {
1569 // first, let's check if the list of Directories has changed
1570 final List<String> directories = getDirs();
1572 boolean needsNewInput = tv.getInput() == null;
1573 List<RepositoryTreeNode<Repository>> oldInput = (List) tv
1574 .getInput();
1575 if (!needsNewInput)
1576 needsNewInput = oldInput.size() != directories.size();
1578 if (!needsNewInput) {
1579 List<String> oldDirectories = new ArrayList<String>();
1580 for (RepositoryTreeNode<Repository> node : oldInput) {
1581 oldDirectories.add(node.getRepository().getDirectory()
1582 .getPath());
1584 needsNewInput = !directories.containsAll(oldDirectories);
1587 final boolean updateInput = needsNewInput;
1588 final List<RepositoryTreeNode<Repository>> newInput;
1589 if (updateInput) {
1590 unregisterRepositoryListener();
1591 try {
1592 newInput = getRepositoriesFromDirs(monitor);
1593 } catch (InterruptedException e) {
1594 return new Status(IStatus.ERROR, Activator
1595 .getPluginId(), e.getMessage(), e);
1597 repositories.clear();
1598 for (RepositoryTreeNode<Repository> node: newInput) {
1599 Repository repo = node.getRepository();
1600 repositories.add(repo);
1601 // add listener if not already added
1602 repo.removeRepositoryChangedListener(repositoryListener);
1603 repo.addRepositoryChangedListener(repositoryListener);
1605 } else {
1606 newInput = null;
1608 Display.getDefault().asyncExec(new Runnable() {
1609 public void run() {
1610 // keep expansion state and selection so that we can
1611 // restore the tree
1612 // after update
1613 Object[] expanded = tv.getExpandedElements();
1614 IStructuredSelection sel = (IStructuredSelection) tv
1615 .getSelection();
1617 if (updateInput)
1618 tv.setInput(newInput);
1619 else
1620 tv.refresh();
1621 tv.setExpandedElements(expanded);
1623 Object selected = sel.getFirstElement();
1624 if (selected != null)
1625 tv.reveal(selected);
1627 IViewPart part = PlatformUI.getWorkbench()
1628 .getActiveWorkbenchWindow().getActivePage()
1629 .findView(IPageLayout.ID_PROP_SHEET);
1630 if (part != null) {
1631 PropertySheet sheet = (PropertySheet) part;
1632 PropertySheetPage page = (PropertySheetPage) sheet
1633 .getCurrentPage();
1634 page.refresh();
1638 return new Status(IStatus.OK, Activator.getPluginId(), ""); //$NON-NLS-1$
1642 job.setSystem(true);
1644 IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService) getSite()
1645 .getService(IWorkbenchSiteProgressService.class);
1647 service.schedule(job);
1649 scheduledJob = job;
1653 private void createRepositoryChangedListener() {
1654 repositoryListener = new RepositoryListener() {
1655 public void refsChanged(RefsChangedEvent e) {
1656 scheduleRefresh();
1659 public void indexChanged(IndexChangedEvent e) {
1660 scheduleRefresh();
1665 private void unregisterRepositoryListener() {
1666 for (Repository repo:repositories)
1667 repo.removeRepositoryChangedListener(repositoryListener);
1672 * Adds a directory to the list if it is not already there
1674 * @param file
1675 * @return see {@link Collection#add(Object)}
1677 public static boolean addDir(File file) {
1679 String dirString;
1680 try {
1681 dirString = file.getCanonicalPath();
1682 } catch (IOException e) {
1683 dirString = file.getAbsolutePath();
1686 List<String> dirStrings = getDirs();
1687 if (dirStrings.contains(dirString)) {
1688 return false;
1689 } else {
1690 Set<String> dirs = new HashSet<String>();
1691 dirs.addAll(dirStrings);
1692 dirs.add(dirString);
1693 saveDirs(dirs);
1694 return true;
1699 * Converts the directories as configured for this view into a list of
1700 * {@link Repository} objects suitable for the tree content provider
1701 * <p>
1702 * TODO move to some utility class
1704 * @param monitor
1705 * @return a list of nodes
1706 * @throws InterruptedException
1708 public static List<RepositoryTreeNode<Repository>> getRepositoriesFromDirs(
1709 IProgressMonitor monitor) throws InterruptedException {
1711 List<String> gitDirStrings = getDirs();
1712 List<RepositoryTreeNode<Repository>> input = new ArrayList<RepositoryTreeNode<Repository>>();
1714 for (String dirString : gitDirStrings) {
1715 if (monitor != null && monitor.isCanceled()) {
1716 throw new InterruptedException(
1717 UIText.RepositoriesView_ActionCanceled_Message);
1719 try {
1720 File dir = new File(dirString);
1721 if (dir.exists() && dir.isDirectory()) {
1722 Repository repo = org.eclipse.egit.core.Activator
1723 .getDefault().getRepositoryCache()
1724 .lookupRepository(dir);
1725 RepositoryNode node = new RepositoryNode(null, repo);
1726 input.add(node);
1728 } catch (IOException e) {
1729 IStatus error = new Status(IStatus.ERROR, Activator
1730 .getPluginId(), e.getMessage(), e);
1731 Activator.getDefault().getLog().log(error);
1734 Collections.sort(input);
1735 return input;
1738 private static void saveDirs(Set<String> gitDirStrings) {
1739 StringBuilder sb = new StringBuilder();
1740 for (String gitDirString : gitDirStrings) {
1741 sb.append(gitDirString);
1742 sb.append(File.pathSeparatorChar);
1745 IEclipsePreferences prefs = getPrefs();
1746 prefs.put(PREFS_DIRECTORIES, sb.toString());
1747 try {
1748 prefs.flush();
1749 } catch (BackingStoreException e) {
1750 IStatus error = new Status(IStatus.ERROR, Activator.getPluginId(),
1751 e.getMessage(), e);
1752 Activator.getDefault().getLog().log(error);
1756 @Override
1757 public void setFocus() {
1758 tv.getTree().setFocus();
1761 @SuppressWarnings("boxing")
1762 private boolean confirmProjectDeletion(List<IProject> projectsToDelete) {
1763 boolean confirmed;
1764 confirmed = MessageDialog
1765 .openConfirm(
1766 getSite().getShell(),
1767 UIText.RepositoriesView_ConfirmProjectDeletion_WindowTitle,
1769 .bind(
1770 UIText.RepositoriesView_ConfirmProjectDeletion_Question,
1771 projectsToDelete.size()));
1772 return confirmed;
1775 public void addSelectionChangedListener(ISelectionChangedListener listener) {
1776 selectionListeners.add(listener);
1779 public ISelection getSelection() {
1780 return currentSelection;
1783 public void removeSelectionChangedListener(
1784 ISelectionChangedListener listener) {
1785 selectionListeners.remove(listener);
1789 public void setSelection(ISelection selection) {
1790 currentSelection = selection;
1791 for (ISelectionChangedListener listener : selectionListeners) {
1792 listener.selectionChanged(new SelectionChangedEvent(
1793 RepositoriesView.this, selection));
1798 * Opens the tree and marks the folder to which a project is pointing
1800 * @param resource
1801 * TODO exceptions?
1803 @SuppressWarnings("unchecked")
1804 public void showResource(final IResource resource) {
1805 IProject project = resource.getProject();
1806 RepositoryMapping mapping = RepositoryMapping.getMapping(project);
1807 if (mapping == null)
1808 return;
1810 if (addDir(mapping.getRepository().getDirectory())) {
1811 scheduleRefresh();
1814 boolean doSetSelection = false;
1816 if (this.scheduledJob != null) {
1817 int state = this.scheduledJob.getState();
1818 if (state == Job.WAITING || state == Job.RUNNING) {
1819 this.scheduledJob.addJobChangeListener(new JobChangeAdapter() {
1821 @Override
1822 public void done(IJobChangeEvent event) {
1823 showResource(resource);
1826 } else {
1827 doSetSelection = true;
1831 if (doSetSelection) {
1832 RepositoriesViewContentProvider cp = (RepositoriesViewContentProvider) tv
1833 .getContentProvider();
1834 RepositoryTreeNode currentNode = null;
1835 Object[] repos = cp.getElements(tv.getInput());
1836 for (Object repo : repos) {
1837 RepositoryTreeNode node = (RepositoryTreeNode) repo;
1838 // TODO equals implementation of Repository?
1839 if (mapping.getRepository().getDirectory().equals(
1840 ((Repository) node.getObject()).getDirectory())) {
1841 for (Object child : cp.getChildren(node)) {
1842 RepositoryTreeNode childNode = (RepositoryTreeNode) child;
1843 if (childNode.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1844 currentNode = childNode;
1845 break;
1848 break;
1852 IPath relPath = new Path(mapping.getRepoRelativePath(resource));
1854 for (String segment : relPath.segments()) {
1855 for (Object child : cp.getChildren(currentNode)) {
1856 RepositoryTreeNode<File> childNode = (RepositoryTreeNode<File>) child;
1857 if (childNode.getObject().getName().equals(segment)) {
1858 currentNode = childNode;
1859 break;
1864 final RepositoryTreeNode selNode = currentNode;
1866 Display.getDefault().asyncExec(new Runnable() {
1868 public void run() {
1869 tv.setSelection(new StructuredSelection(selNode), true);
1877 public boolean show(ShowInContext context) {
1878 ISelection selection = context.getSelection();
1879 if (selection instanceof IStructuredSelection) {
1880 IStructuredSelection ss = (IStructuredSelection) selection;
1881 if (ss.size() == 1) {
1882 Object element = ss.getFirstElement();
1883 if (element instanceof IAdaptable) {
1884 IResource resource = (IResource) ((IAdaptable) element)
1885 .getAdapter(IResource.class);
1886 if (resource != null) {
1887 showResource(resource);
1888 return true;
1893 return false;
1896 private void removeRepository(final IProgressMonitor monitor,
1897 final Repository... repository) {
1898 final List<IProject> projectsToDelete = new ArrayList<IProject>();
1900 monitor
1901 .setTaskName(UIText.RepositoriesView_DeleteRepoDeterminProjectsMessage);
1903 for (Repository repo : repository) {
1904 File workDir = repo.getWorkDir();
1905 final IPath wdPath = new Path(workDir.getAbsolutePath());
1906 for (IProject prj : ResourcesPlugin.getWorkspace().getRoot()
1907 .getProjects()) {
1908 if (monitor.isCanceled())
1909 return;
1910 if (wdPath.isPrefixOf(prj.getLocation())) {
1911 projectsToDelete.add(prj);
1914 repo.removeRepositoryChangedListener(repositoryListener);
1917 if (!projectsToDelete.isEmpty()) {
1918 boolean confirmed;
1919 confirmed = confirmProjectDeletion(projectsToDelete);
1920 if (!confirmed) {
1921 return;
1925 if (monitor.isCanceled())
1926 return;
1928 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
1930 public void run(IProgressMonitor actMonitor) throws CoreException {
1932 for (IProject prj : projectsToDelete) {
1933 prj.delete(false, false, actMonitor);
1935 for (Repository repo : repository)
1936 removeDir(repo.getDirectory());
1937 scheduleRefresh();
1941 try {
1942 ResourcesPlugin.getWorkspace().run(wsr,
1943 ResourcesPlugin.getWorkspace().getRoot(),
1944 IWorkspace.AVOID_UPDATE, monitor);
1945 } catch (CoreException e1) {
1946 Activator.logError(e1.getMessage(), e1);