Git Repositories View: Copy/Paste support
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositoriesView.java
blob7486aa55b04a7806ad9ff920ccc58747ada95b9b
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.IProjectDescription;
30 import org.eclipse.core.resources.IResource;
31 import org.eclipse.core.resources.IWorkspace;
32 import org.eclipse.core.resources.IWorkspaceRunnable;
33 import org.eclipse.core.resources.ResourcesPlugin;
34 import org.eclipse.core.runtime.CoreException;
35 import org.eclipse.core.runtime.IAdaptable;
36 import org.eclipse.core.runtime.IPath;
37 import org.eclipse.core.runtime.IProgressMonitor;
38 import org.eclipse.core.runtime.IStatus;
39 import org.eclipse.core.runtime.NullProgressMonitor;
40 import org.eclipse.core.runtime.Path;
41 import org.eclipse.core.runtime.Status;
42 import org.eclipse.core.runtime.SubProgressMonitor;
43 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
44 import org.eclipse.core.runtime.jobs.Job;
45 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
46 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
47 import org.eclipse.core.runtime.preferences.InstanceScope;
48 import org.eclipse.egit.core.op.BranchOperation;
49 import org.eclipse.egit.core.op.ConnectProviderOperation;
50 import org.eclipse.egit.core.project.RepositoryMapping;
51 import org.eclipse.egit.ui.Activator;
52 import org.eclipse.egit.ui.UIIcons;
53 import org.eclipse.egit.ui.UIText;
54 import org.eclipse.egit.ui.internal.clone.GitCloneWizard;
55 import org.eclipse.egit.ui.internal.clone.GitCreateProjectViaWizardWizard;
56 import org.eclipse.egit.ui.internal.repository.RepositoryTreeNode.RepositoryTreeNodeType;
57 import org.eclipse.jface.action.Action;
58 import org.eclipse.jface.action.IAction;
59 import org.eclipse.jface.dialogs.MessageDialog;
60 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
61 import org.eclipse.jface.operation.IRunnableWithProgress;
62 import org.eclipse.jface.viewers.IOpenListener;
63 import org.eclipse.jface.viewers.ISelection;
64 import org.eclipse.jface.viewers.ISelectionChangedListener;
65 import org.eclipse.jface.viewers.ISelectionProvider;
66 import org.eclipse.jface.viewers.IStructuredSelection;
67 import org.eclipse.jface.viewers.ITreeContentProvider;
68 import org.eclipse.jface.viewers.OpenEvent;
69 import org.eclipse.jface.viewers.SelectionChangedEvent;
70 import org.eclipse.jface.viewers.StructuredSelection;
71 import org.eclipse.jface.viewers.TreeViewer;
72 import org.eclipse.jface.window.Window;
73 import org.eclipse.jface.wizard.Wizard;
74 import org.eclipse.jface.wizard.WizardDialog;
75 import org.eclipse.jgit.lib.Constants;
76 import org.eclipse.jgit.lib.Ref;
77 import org.eclipse.jgit.lib.RefUpdate;
78 import org.eclipse.jgit.lib.Repository;
79 import org.eclipse.jgit.lib.RepositoryCache;
80 import org.eclipse.jgit.lib.RepositoryConfig;
81 import org.eclipse.jgit.transport.RemoteConfig;
82 import org.eclipse.osgi.util.NLS;
83 import org.eclipse.swt.SWT;
84 import org.eclipse.swt.dnd.Clipboard;
85 import org.eclipse.swt.dnd.TextTransfer;
86 import org.eclipse.swt.dnd.Transfer;
87 import org.eclipse.swt.events.MenuDetectEvent;
88 import org.eclipse.swt.events.MenuDetectListener;
89 import org.eclipse.swt.events.SelectionAdapter;
90 import org.eclipse.swt.events.SelectionEvent;
91 import org.eclipse.swt.graphics.Point;
92 import org.eclipse.swt.widgets.Composite;
93 import org.eclipse.swt.widgets.Display;
94 import org.eclipse.swt.widgets.Menu;
95 import org.eclipse.swt.widgets.MenuItem;
96 import org.eclipse.swt.widgets.TreeItem;
97 import org.eclipse.ui.IEditorInput;
98 import org.eclipse.ui.IEditorPart;
99 import org.eclipse.ui.IFileEditorInput;
100 import org.eclipse.ui.IPageLayout;
101 import org.eclipse.ui.ISelectionListener;
102 import org.eclipse.ui.ISelectionService;
103 import org.eclipse.ui.IViewPart;
104 import org.eclipse.ui.IWorkbenchPart;
105 import org.eclipse.ui.PartInitException;
106 import org.eclipse.ui.PlatformUI;
107 import org.eclipse.ui.actions.ActionFactory;
108 import org.eclipse.ui.editors.text.EditorsUI;
109 import org.eclipse.ui.ide.FileStoreEditorInput;
110 import org.eclipse.ui.ide.IDE;
111 import org.eclipse.ui.part.IShowInTarget;
112 import org.eclipse.ui.part.ShowInContext;
113 import org.eclipse.ui.part.ViewPart;
114 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
115 import org.eclipse.ui.views.properties.IPropertySheetPage;
116 import org.eclipse.ui.views.properties.PropertySheet;
117 import org.eclipse.ui.views.properties.PropertySheetPage;
118 import org.osgi.service.prefs.BackingStoreException;
122 * The Git Repositories view.
123 * <p>
124 * This keeps track of a bunch of local directory names each of which represent
125 * a Git Repository. This list is stored in some Preferences object and used to
126 * build the tree in the view.
127 * <p>
128 * Implements {@link ISelectionProvider} in order to integrate with the
129 * Properties view.
130 * <p>
131 * TODO
132 * <li>Clarification whether to show projects, perhaps configurable switch</li>
135 public class RepositoriesView extends ViewPart implements ISelectionProvider,
136 IShowInTarget {
138 /** The view ID */
139 public static final String VIEW_ID = "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
141 // TODO central constants? RemoteConfig ones are private
142 static final String REMOTE = "remote"; //$NON-NLS-1$
144 static final String URL = "url"; //$NON-NLS-1$
146 static final String PUSHURL = "pushurl"; //$NON-NLS-1$
148 static final String FETCH = "fetch"; //$NON-NLS-1$
150 static final String PUSH = "push"; //$NON-NLS-1$
152 private static final String PREFS_DIRECTORIES = "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
154 private static final String PREFS_SYNCED = "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
156 private final List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>();
158 private ISelection currentSelection = new StructuredSelection();
160 private Job scheduledJob;
162 private TreeViewer tv;
164 private IAction importAction;
166 private IAction addAction;
168 private IAction refreshAction;
170 private IAction linkWithSelectionAction;
172 private IAction copyAction;
174 private IAction pasteAction;
177 * TODO move to utility class
179 * @return the directories as configured for this view
181 public static List<String> getDirs() {
182 List<String> resultStrings = new ArrayList<String>();
183 String dirs = getPrefs().get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
184 if (dirs != null && dirs.length() > 0) {
185 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
186 while (tok.hasMoreTokens()) {
187 String dirName = tok.nextToken();
188 File testFile = new File(dirName);
189 if (testFile.exists()) {
190 resultStrings.add(dirName);
194 Collections.sort(resultStrings);
195 return resultStrings;
198 private static void removeDir(File file) {
200 String dir;
201 try {
202 dir = file.getCanonicalPath();
203 } catch (IOException e1) {
204 dir = file.getAbsolutePath();
207 IEclipsePreferences prefs = getPrefs();
209 TreeSet<String> resultStrings = new TreeSet<String>();
210 String dirs = prefs.get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
211 if (dirs != null && dirs.length() > 0) {
212 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
213 while (tok.hasMoreTokens()) {
214 String dirName = tok.nextToken();
215 File testFile = new File(dirName);
216 if (testFile.exists()) {
217 try {
218 resultStrings.add(testFile.getCanonicalPath());
219 } catch (IOException e) {
220 resultStrings.add(testFile.getAbsolutePath());
226 if (resultStrings.remove(dir)) {
227 StringBuilder sb = new StringBuilder();
228 for (String gitDirString : resultStrings) {
229 sb.append(gitDirString);
230 sb.append(File.pathSeparatorChar);
233 prefs.put(PREFS_DIRECTORIES, sb.toString());
234 try {
235 prefs.flush();
236 } catch (BackingStoreException e) {
237 IStatus error = new Status(IStatus.ERROR, Activator
238 .getPluginId(), e.getMessage(), e);
239 Activator.getDefault().getLog().log(error);
245 @Override
246 public Object getAdapter(Class adapter) {
247 // integrate with Properties view
248 if (adapter == IPropertySheetPage.class) {
249 PropertySheetPage page = new PropertySheetPage();
250 page
251 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
252 page));
253 return page;
256 return super.getAdapter(adapter);
259 @Override
260 public void createPartControl(Composite parent) {
261 tv = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
262 tv.setContentProvider(new RepositoriesViewContentProvider());
263 // the label provider registers itself
264 new RepositoriesViewLabelProvider(tv);
266 getSite().setSelectionProvider(this);
268 tv.addSelectionChangedListener(new ISelectionChangedListener() {
270 public void selectionChanged(SelectionChangedEvent event) {
272 copyAction.setEnabled(false);
274 IStructuredSelection ssel = (IStructuredSelection) event
275 .getSelection();
276 if (ssel.size() == 1) {
277 RepositoryTreeNode node = (RepositoryTreeNode) ssel
278 .getFirstElement();
279 // allow copy on repository, file, or folder (copying the
280 // directory)
281 if (node.getType() == RepositoryTreeNodeType.REPO
282 || node.getType() == RepositoryTreeNodeType.WORKINGDIR
283 || node.getType() == RepositoryTreeNodeType.FOLDER
284 || node.getType() == RepositoryTreeNodeType.FILE) {
285 copyAction.setEnabled(true);
287 setSelection(new StructuredSelection(ssel.getFirstElement()));
288 } else {
289 setSelection(new StructuredSelection());
294 tv.addOpenListener(new IOpenListener() {
295 public void open(OpenEvent event) {
296 IStructuredSelection selection = (IStructuredSelection) event
297 .getSelection();
298 if (selection.isEmpty()) {
299 // nothing selected, ignore
300 return;
303 Object element = selection.getFirstElement();
304 ITreeContentProvider contentProvider = (ITreeContentProvider) tv
305 .getContentProvider();
306 if (contentProvider.hasChildren(element)) {
307 // this element has children, expand/collapse it
308 tv.setExpandedState(element, !tv.getExpandedState(element));
309 } else {
310 Object[] selectionArray = selection.toArray();
311 for (Object selectedElement : selectionArray) {
312 RepositoryTreeNode node = (RepositoryTreeNode) selectedElement;
313 // if any of the selected elements are not files, ignore
314 // the open request
315 if (node.getType() != RepositoryTreeNodeType.FILE
316 && node.getType() != RepositoryTreeNodeType.REF) {
317 return;
321 // open the files the user has selected
322 for (Object selectedElement : selectionArray) {
323 RepositoryTreeNode node = (RepositoryTreeNode) selectedElement;
324 if (node.getType() == RepositoryTreeNodeType.FILE)
325 openFile((File) node.getObject());
326 else if (node.getType() == RepositoryTreeNodeType.REF) {
327 Ref ref = (Ref) node.getObject();
328 if (ref.getName().startsWith(Constants.R_HEADS)
329 || ref.getName().startsWith(
330 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) {
393 tv.getTree().setMenu(null);
394 Menu men = new Menu(tv.getTree());
396 TreeItem testItem = tv.getTree().getItem(
397 tv.getTree().toControl(new Point(e.x, e.y)));
398 if (testItem == null) {
399 addMenuItemsForPanel(men);
400 } else {
401 addMenuItemsForTreeSelection(men);
404 tv.getTree().setMenu(men);
409 private void addMenuItemsForPanel(Menu men) {
411 MenuItem importItem = new MenuItem(men, SWT.PUSH);
412 importItem.setText(UIText.RepositoriesView_ImportRepository_MenuItem);
413 importItem.addSelectionListener(new SelectionAdapter() {
415 @Override
416 public void widgetSelected(SelectionEvent e) {
417 importAction.run();
422 MenuItem addItem = new MenuItem(men, SWT.PUSH);
423 addItem.setText(UIText.RepositoriesView_AddRepository_MenuItem);
424 addItem.addSelectionListener(new SelectionAdapter() {
426 @Override
427 public void widgetSelected(SelectionEvent e) {
428 addAction.run();
433 MenuItem pasteItem = new MenuItem(men, SWT.PUSH);
434 pasteItem.setText(UIText.RepositoriesView_PasteMenu);
435 pasteItem.addSelectionListener(new SelectionAdapter() {
437 @Override
438 public void widgetSelected(SelectionEvent e) {
439 pasteAction.run();
444 MenuItem refreshItem = new MenuItem(men, SWT.PUSH);
445 refreshItem.setText(refreshAction.getText());
446 refreshItem.addSelectionListener(new SelectionAdapter() {
448 @Override
449 public void widgetSelected(SelectionEvent e) {
450 refreshAction.run();
457 @SuppressWarnings("unchecked")
458 private void addMenuItemsForTreeSelection(Menu men) {
460 final IStructuredSelection sel = (IStructuredSelection) tv
461 .getSelection();
463 boolean importableProjectsOnly = true;
465 for (Object node : sel.toArray()) {
466 RepositoryTreeNode tnode = (RepositoryTreeNode) node;
467 importableProjectsOnly = tnode.getType() == RepositoryTreeNodeType.PROJ;
468 if (!importableProjectsOnly)
469 break;
472 if (importableProjectsOnly) {
473 MenuItem sync = new MenuItem(men, SWT.PUSH);
474 sync.setText(UIText.RepositoriesView_ImportProject_MenuItem);
476 sync.addSelectionListener(new SelectionAdapter() {
478 @Override
479 public void widgetSelected(SelectionEvent e) {
481 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
483 public void run(IProgressMonitor monitor)
484 throws CoreException {
486 for (Object selected : sel.toArray()) {
487 RepositoryTreeNode<File> projectNode = (RepositoryTreeNode<File>) selected;
488 File file = projectNode.getObject();
490 IProjectDescription pd = ResourcesPlugin
491 .getWorkspace().newProjectDescription(
492 file.getName());
493 IPath locationPath = new Path(file
494 .getAbsolutePath());
496 pd.setLocation(locationPath);
498 ResourcesPlugin.getWorkspace().getRoot()
499 .getProject(pd.getName()).create(pd,
500 monitor);
501 IProject project = ResourcesPlugin
502 .getWorkspace().getRoot().getProject(
503 pd.getName());
504 project.open(monitor);
506 File gitDir = projectNode.getRepository()
507 .getDirectory();
509 ConnectProviderOperation connectProviderOperation = new ConnectProviderOperation(
510 project, gitDir);
511 connectProviderOperation
512 .execute(new SubProgressMonitor(
513 monitor, 20));
520 try {
522 ResourcesPlugin.getWorkspace().run(wsr,
523 ResourcesPlugin.getWorkspace().getRoot(),
524 IWorkspace.AVOID_UPDATE,
525 new NullProgressMonitor());
527 scheduleRefresh();
528 } catch (CoreException e1) {
529 Activator.logError(e1.getMessage(), e1);
537 // from here on, we only deal with single selection
538 if (sel.size() > 1)
539 return;
541 final RepositoryTreeNode node = (RepositoryTreeNode) sel
542 .getFirstElement();
544 if (node.getType() == RepositoryTreeNodeType.REF) {
546 final Ref ref = (Ref) node.getObject();
548 // we don't check out symbolic references
549 if (!ref.isSymbolic()) {
551 MenuItem checkout = new MenuItem(men, SWT.PUSH);
552 checkout.setText(UIText.RepositoriesView_CheckOut_MenuItem);
554 try {
555 if (node.getRepository().getFullBranch().equals(
556 ref.getName())) {
557 // no checkout on current branch
558 checkout.setEnabled(false);
560 } catch (IOException e2) {
561 // ignore
564 checkout.addSelectionListener(new SelectionAdapter() {
566 @Override
567 public void widgetSelected(SelectionEvent e) {
568 checkoutBranch(node, ref.getLeaf().getName());
572 new MenuItem(men, SWT.SEPARATOR);
574 createCreateBranchItem(men, node);
575 createDeleteBranchItem(men, node);
580 if (node.getType() == RepositoryTreeNodeType.LOCALBRANCHES
581 || node.getType() == RepositoryTreeNodeType.REMOTEBRANCHES)
582 createCreateBranchItem(men, node);
584 // for Repository: import existing projects, remove, (delete), open
585 // properties
586 if (node.getType() == RepositoryTreeNodeType.REPO) {
588 final Repository repo = (Repository) node.getObject();
590 // TODO "import existing plug-in" menu item
592 MenuItem remove = new MenuItem(men, SWT.PUSH);
593 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
594 remove.addSelectionListener(new SelectionAdapter() {
596 @Override
597 public void widgetSelected(SelectionEvent e) {
599 List<IProject> projectsToDelete = new ArrayList<IProject>();
600 File workDir = repo.getWorkDir();
601 final IPath wdPath = new Path(workDir.getAbsolutePath());
602 for (IProject prj : ResourcesPlugin.getWorkspace()
603 .getRoot().getProjects()) {
604 if (wdPath.isPrefixOf(prj.getLocation())) {
605 projectsToDelete.add(prj);
609 if (!projectsToDelete.isEmpty()) {
610 boolean confirmed;
611 confirmed = confirmProjectDeletion(projectsToDelete);
612 if (!confirmed) {
613 return;
617 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
619 public void run(IProgressMonitor monitor)
620 throws CoreException {
622 for (IProject prj : ResourcesPlugin.getWorkspace()
623 .getRoot().getProjects()) {
624 if (wdPath.isPrefixOf(prj.getLocation())) {
625 prj.delete(false, false, monitor);
629 removeDir(repo.getDirectory());
630 scheduleRefresh();
634 try {
635 ResourcesPlugin.getWorkspace().run(wsr,
636 ResourcesPlugin.getWorkspace().getRoot(),
637 IWorkspace.AVOID_UPDATE,
638 new NullProgressMonitor());
639 } catch (CoreException e1) {
640 Activator.logError(e1.getMessage(), e1);
647 // TODO delete does not work because of file locks on .pack-files
648 // Shawn Pearce has added the following thoughts:
650 // Hmm. We probably can't active detect file locks on pack files on
651 // Windows, can we?
652 // It would be nice if we could support a delete, but only if the
653 // repository is
654 // reasonably believed to be not-in-use right now.
656 // Within EGit you might be able to check GitProjectData and its
657 // repositoryCache to
658 // see if the repository is open by this workspace. If it is, then
659 // we know we shouldn't
660 // try to delete it.
662 // Some coding might look like this:
664 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
665 // deleteRepo.setText("Delete");
666 // deleteRepo.addSelectionListener(new SelectionAdapter() {
668 // @Override
669 // public void widgetSelected(SelectionEvent e) {
671 // boolean confirmed = MessageDialog.openConfirm(getSite()
672 // .getShell(), "Confirm",
673 // "This will delete the repository, continue?");
675 // if (!confirmed)
676 // return;
678 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
680 // public void run(IProgressMonitor monitor)
681 // throws CoreException {
682 // File workDir = repos.get(0).getRepository()
683 // .getWorkDir();
685 // File gitDir = repos.get(0).getRepository()
686 // .getDirectory();
688 // IPath wdPath = new Path(workDir.getAbsolutePath());
689 // for (IProject prj : ResourcesPlugin.getWorkspace()
690 // .getRoot().getProjects()) {
691 // if (wdPath.isPrefixOf(prj.getLocation())) {
692 // prj.delete(false, false, monitor);
693 // }
694 // }
696 // repos.get(0).getRepository().close();
698 // boolean deleted = deleteRecursively(gitDir, monitor);
699 // if (!deleted) {
700 // MessageDialog.openError(getSite().getShell(),
701 // "Error",
702 // "Could not delete Git Repository");
703 // }
705 // deleted = deleteRecursively(workDir, monitor);
706 // if (!deleted) {
707 // MessageDialog
708 // .openError(getSite().getShell(),
709 // "Error",
710 // "Could not delete Git Working Directory");
711 // }
713 // scheduleRefresh();
714 // }
716 // private boolean deleteRecursively(File fileToDelete,
717 // IProgressMonitor monitor) {
718 // if (fileToDelete.isDirectory()) {
719 // for (File file : fileToDelete.listFiles()) {
720 // if (!deleteRecursively(file, monitor)) {
721 // return false;
722 // }
723 // }
724 // }
725 // monitor.setTaskName(fileToDelete.getAbsolutePath());
726 // boolean deleted = fileToDelete.delete();
727 // if (!deleted) {
728 // System.err.println("Could not delete "
729 // + fileToDelete.getAbsolutePath());
730 // }
731 // return deleted;
732 // }
733 // };
735 // try {
736 // ResourcesPlugin.getWorkspace().run(wsr,
737 // ResourcesPlugin.getWorkspace().getRoot(),
738 // IWorkspace.AVOID_UPDATE,
739 // new NullProgressMonitor());
740 // } catch (CoreException e1) {
741 // // TODO Exception handling
742 // e1.printStackTrace();
743 // }
745 // }
747 // });
749 new MenuItem(men, SWT.SEPARATOR);
751 createImportProjectItem(men, repo, repo.getWorkDir().getPath());
753 new MenuItem(men, SWT.SEPARATOR);
755 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
756 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
757 openPropsView.addSelectionListener(new SelectionAdapter() {
759 @Override
760 public void widgetSelected(SelectionEvent e) {
761 try {
762 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
763 .getActivePage().showView(
764 IPageLayout.ID_PROP_SHEET);
765 } catch (PartInitException e1) {
766 // just ignore
772 new MenuItem(men, SWT.SEPARATOR);
774 createCopyPathItem(men, repo.getDirectory().getPath());
777 if (node.getType() == RepositoryTreeNodeType.REMOTES) {
779 MenuItem remoteConfig = new MenuItem(men, SWT.PUSH);
780 remoteConfig.setText(UIText.RepositoriesView_NewRemoteMenu);
781 remoteConfig.addSelectionListener(new SelectionAdapter() {
783 @Override
784 public void widgetSelected(SelectionEvent e) {
786 WizardDialog dlg = new WizardDialog(getSite().getShell(),
787 new NewRemoteWizard(node.getRepository()));
788 if (dlg.open() == Window.OK)
789 scheduleRefresh();
796 if (node.getType() == RepositoryTreeNodeType.REMOTE) {
798 final String configName = (String) node.getObject();
800 RemoteConfig rconfig;
801 try {
802 rconfig = new RemoteConfig(node.getRepository().getConfig(),
803 configName);
804 } catch (URISyntaxException e2) {
805 // TODO Exception handling
806 rconfig = null;
809 boolean fetchExists = rconfig != null
810 && !rconfig.getURIs().isEmpty();
811 boolean pushExists = rconfig != null
812 && !rconfig.getPushURIs().isEmpty();
814 if (!fetchExists) {
815 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
816 configureUrlFetch
817 .setText(UIText.RepositoriesView_CreateFetch_menu);
819 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
821 @Override
822 public void widgetSelected(SelectionEvent e) {
824 WizardDialog dlg = new WizardDialog(getSite()
825 .getShell(), new ConfigureRemoteWizard(node
826 .getRepository(), configName, false));
827 if (dlg.open() == Window.OK)
828 scheduleRefresh();
835 if (!pushExists) {
836 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
838 configureUrlPush
839 .setText(UIText.RepositoriesView_CreatePush_menu);
841 configureUrlPush.addSelectionListener(new SelectionAdapter() {
843 @Override
844 public void widgetSelected(SelectionEvent e) {
846 WizardDialog dlg = new WizardDialog(getSite()
847 .getShell(), new ConfigureRemoteWizard(node
848 .getRepository(), configName, true));
849 if (dlg.open() == Window.OK)
850 scheduleRefresh();
857 if (!fetchExists || !pushExists)
858 // add a separator dynamically
859 new MenuItem(men, SWT.SEPARATOR);
861 MenuItem removeRemote = new MenuItem(men, SWT.PUSH);
862 removeRemote.setText(UIText.RepositoriesView_RemoveRemoteMenu);
863 removeRemote.addSelectionListener(new SelectionAdapter() {
865 @Override
866 public void widgetSelected(SelectionEvent e) {
868 boolean ok = MessageDialog
869 .openConfirm(
870 getSite().getShell(),
871 UIText.RepositoriesView_ConfirmDeleteRemoteHeader,
873 .bind(
874 UIText.RepositoriesView_ConfirmDeleteRemoteMessage,
875 configName));
876 if (ok) {
877 RepositoryConfig config = node.getRepository()
878 .getConfig();
879 config.unsetSection(REMOTE, configName);
880 try {
881 config.save();
882 scheduleRefresh();
883 } catch (IOException e1) {
884 Activator.handleError(
885 UIText.RepositoriesView_ErrorHeader, e1,
886 true);
894 new MenuItem(men, SWT.SEPARATOR);
896 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
897 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
898 openPropsView.addSelectionListener(new SelectionAdapter() {
900 @Override
901 public void widgetSelected(SelectionEvent e) {
902 try {
903 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
904 .getActivePage().showView(
905 IPageLayout.ID_PROP_SHEET);
906 } catch (PartInitException e1) {
907 // just ignore
914 if (node.getType() == RepositoryTreeNodeType.FETCH) {
916 final String configName = (String) node.getParent().getObject();
918 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
919 configureUrlFetch
920 .setText(UIText.RepositoriesView_ConfigureFetchMenu);
922 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
924 @Override
925 public void widgetSelected(SelectionEvent e) {
927 WizardDialog dlg = new WizardDialog(getSite().getShell(),
928 new ConfigureRemoteWizard(node.getRepository(),
929 configName, false));
930 if (dlg.open() == Window.OK)
931 scheduleRefresh();
937 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
938 deleteFetch.setText(UIText.RepositoriesView_RemoveFetch_menu);
939 deleteFetch.addSelectionListener(new SelectionAdapter() {
941 @Override
942 public void widgetSelected(SelectionEvent e) {
943 RepositoryConfig config = node.getRepository().getConfig();
944 config.unset("remote", configName, "url"); //$NON-NLS-1$ //$NON-NLS-2$
945 config.unset("remote", configName, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
946 try {
947 config.save();
948 scheduleRefresh();
949 } catch (IOException e1) {
950 MessageDialog.openError(getSite().getShell(),
951 UIText.RepositoriesView_ErrorHeader, e1
952 .getMessage());
960 if (node.getType() == RepositoryTreeNodeType.PUSH) {
962 final String configName = (String) node.getParent().getObject();
964 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
966 configureUrlPush.setText(UIText.RepositoriesView_ConfigurePushMenu);
968 configureUrlPush.addSelectionListener(new SelectionAdapter() {
970 @Override
971 public void widgetSelected(SelectionEvent e) {
973 WizardDialog dlg = new WizardDialog(getSite().getShell(),
974 new ConfigureRemoteWizard(node.getRepository(),
975 configName, true));
976 if (dlg.open() == Window.OK)
977 scheduleRefresh();
982 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
983 deleteFetch.setText(UIText.RepositoriesView_RemovePush_menu);
984 deleteFetch.addSelectionListener(new SelectionAdapter() {
986 @Override
987 public void widgetSelected(SelectionEvent e) {
988 RepositoryConfig config = node.getRepository().getConfig();
989 config.unset("remote", configName, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
990 config.unset("remote", configName, "push"); //$NON-NLS-1$ //$NON-NLS-2$
991 try {
992 config.save();
993 scheduleRefresh();
994 } catch (IOException e1) {
995 MessageDialog.openError(getSite().getShell(),
996 UIText.RepositoriesView_ErrorHeader, e1
997 .getMessage());
1004 if (node.getType() == RepositoryTreeNodeType.FILE) {
1006 final File file = (File) node.getObject();
1008 MenuItem openInTextEditor = new MenuItem(men, SWT.PUSH);
1009 openInTextEditor
1010 .setText(UIText.RepositoriesView_OpenInTextEditor_menu);
1011 openInTextEditor.addSelectionListener(new SelectionAdapter() {
1013 @Override
1014 public void widgetSelected(SelectionEvent e) {
1015 openFile(file);
1020 new MenuItem(men, SWT.SEPARATOR);
1021 createCopyPathItem(men, file.getPath());
1024 if (node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1025 String path = node.getRepository().getWorkDir().getAbsolutePath();
1026 createImportProjectItem(men, node.getRepository(), path);
1027 new MenuItem(men, SWT.SEPARATOR);
1028 createCopyPathItem(men, path);
1031 if (node.getType() == RepositoryTreeNodeType.FOLDER) {
1032 String path = ((File) node.getObject()).getPath();
1033 createImportProjectItem(men, node.getRepository(), path);
1034 new MenuItem(men, SWT.SEPARATOR);
1035 createCopyPathItem(men, path);
1040 private void createCopyPathItem(Menu men, final String path) {
1042 MenuItem copyPath;
1043 copyPath = new MenuItem(men, SWT.PUSH);
1044 copyPath.setText(UIText.RepositoriesView_CopyPathToClipboardMenu);
1045 copyPath.addSelectionListener(new SelectionAdapter() {
1047 @Override
1048 public void widgetSelected(SelectionEvent e) {
1049 Clipboard clipboard = new Clipboard(null);
1050 TextTransfer textTransfer = TextTransfer.getInstance();
1051 Transfer[] transfers = new Transfer[] { textTransfer };
1052 Object[] data = new Object[] { path };
1053 clipboard.setContents(data, transfers);
1054 clipboard.dispose();
1061 private void createCreateBranchItem(Menu men, final RepositoryTreeNode node) {
1063 final boolean remoteMode;
1064 final Ref ref;
1065 if (node.getType() == RepositoryTreeNodeType.REF) {
1066 remoteMode = node.getParent().getType() == RepositoryTreeNodeType.REMOTEBRANCHES;
1067 ref = (Ref) node.getObject();
1068 } else if (node.getType() == RepositoryTreeNodeType.LOCALBRANCHES) {
1069 remoteMode = false;
1070 ref = null;
1071 } else if (node.getType() == RepositoryTreeNodeType.REMOTEBRANCHES) {
1072 remoteMode = true;
1073 ref = null;
1074 } else
1075 return;
1077 MenuItem createLocal = new MenuItem(men, SWT.PUSH);
1078 if (remoteMode)
1079 createLocal.setText(UIText.RepositoriesView_NewRemoteBranchMenu);
1080 else
1081 createLocal.setText(UIText.RepositoriesView_NewLocalBranchMenu);
1083 createLocal.addSelectionListener(new SelectionAdapter() {
1085 @Override
1086 public void widgetSelected(SelectionEvent e) {
1088 Wizard wiz = new Wizard() {
1090 @Override
1091 public void addPages() {
1092 addPage(new CreateBranchPage(node.getRepository(), ref,
1093 remoteMode));
1094 setWindowTitle(UIText.RepositoriesView_NewBranchTitle);
1097 @Override
1098 public boolean performFinish() {
1100 try {
1101 getContainer().run(false, true,
1102 new IRunnableWithProgress() {
1104 public void run(IProgressMonitor monitor)
1105 throws InvocationTargetException,
1106 InterruptedException {
1107 CreateBranchPage cp = (CreateBranchPage) getPages()[0];
1108 try {
1109 cp.createBranch(monitor);
1110 } catch (CoreException ce) {
1111 throw new InvocationTargetException(
1112 ce);
1113 } catch (IOException ioe) {
1114 throw new InvocationTargetException(
1115 ioe);
1120 } catch (InvocationTargetException ite) {
1121 Activator
1122 .handleError(
1123 UIText.RepositoriesView_BranchCreationFailureMessage,
1124 ite.getCause(), true);
1125 return false;
1126 } catch (InterruptedException ie) {
1127 // ignore here
1129 return true;
1132 if (new WizardDialog(getSite().getShell(), wiz).open() == Window.OK)
1133 scheduleRefresh();
1140 private void createDeleteBranchItem(Menu men, final RepositoryTreeNode node) {
1142 final Ref ref = (Ref) node.getObject();
1144 MenuItem deleteBranch = new MenuItem(men, SWT.PUSH);
1145 deleteBranch.setText(UIText.RepositoriesView_DeleteBranchMenu);
1147 try {
1148 if (node.getRepository().getFullBranch().equals(ref.getName())) {
1149 deleteBranch.setEnabled(false);
1151 } catch (IOException e2) {
1152 // ignore
1155 deleteBranch.addSelectionListener(new SelectionAdapter() {
1157 @Override
1158 public void widgetSelected(SelectionEvent e) {
1160 if (!MessageDialog
1161 .openConfirm(
1162 getSite().getShell(),
1163 UIText.RepositoriesView_ConfirmDeleteTitle,
1165 .bind(
1166 UIText.RepositoriesView_ConfirmBranchDeletionMessage,
1167 ref.getName())))
1168 return;
1170 try {
1171 new ProgressMonitorDialog(getSite().getShell()).run(false,
1172 false, new IRunnableWithProgress() {
1174 public void run(IProgressMonitor monitor)
1175 throws InvocationTargetException,
1176 InterruptedException {
1178 try {
1179 RefUpdate op = node.getRepository()
1180 .updateRef(ref.getName());
1181 op.setRefLogMessage("branch deleted", //$NON-NLS-1$
1182 false);
1183 // we set the force update in order
1184 // to avoid having this rejected
1185 // due to minor issues
1186 op.setForceUpdate(true);
1187 op.delete();
1188 scheduleRefresh();
1189 } catch (IOException ioe) {
1190 throw new InvocationTargetException(ioe);
1195 } catch (InvocationTargetException e1) {
1196 Activator
1197 .handleError(
1198 UIText.RepositoriesView_BranchDeletionFailureMessage,
1199 e1.getCause(), true);
1200 e1.printStackTrace();
1201 } catch (InterruptedException e1) {
1202 // ignore
1210 private void openFile(File file) {
1211 IFileStore store = EFS.getLocalFileSystem().getStore(
1212 new Path(file.getAbsolutePath()));
1213 try {
1214 // TODO do we need a read-only editor here?
1215 IDE.openEditor(getSite().getPage(),
1216 new FileStoreEditorInput(store),
1217 EditorsUI.DEFAULT_TEXT_EDITOR_ID);
1218 } catch (PartInitException e) {
1219 Activator.handleError(UIText.RepositoriesView_Error_WindowTitle, e,
1220 true);
1224 private void checkoutBranch(final RepositoryTreeNode node,
1225 final String refName) {
1226 // for the sake of UI responsiveness, let's start a job
1227 Job job = new Job(NLS.bind(UIText.RepositoriesView_CheckingOutMessage,
1228 refName)) {
1230 @Override
1231 protected IStatus run(IProgressMonitor monitor) {
1233 Repository repo = node.getRepository();
1235 final BranchOperation op = new BranchOperation(repo, refName);
1236 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
1238 public void run(IProgressMonitor myMonitor)
1239 throws CoreException {
1240 op.execute(myMonitor);
1244 try {
1245 ResourcesPlugin.getWorkspace().run(wsr,
1246 ResourcesPlugin.getWorkspace().getRoot(),
1247 IWorkspace.AVOID_UPDATE, monitor);
1248 scheduleRefresh();
1249 } catch (CoreException e1) {
1250 return new Status(IStatus.ERROR, Activator.getPluginId(),
1251 e1.getMessage(), e1);
1254 return Status.OK_STATUS;
1258 job.setUser(true);
1259 job.schedule();
1262 private void createImportProjectItem(Menu men, final Repository repo,
1263 final String path) {
1265 MenuItem startWizard;
1266 startWizard = new MenuItem(men, SWT.PUSH);
1267 startWizard.setText(UIText.RepositoriesView_ImportProjectsMenu);
1268 startWizard.addSelectionListener(new SelectionAdapter() {
1270 @Override
1271 public void widgetSelected(SelectionEvent e) {
1272 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1273 new GitCreateProjectViaWizardWizard(repo, path));
1274 if (dlg.open() == Window.OK)
1275 scheduleRefresh();
1281 // we could start the ImportWizard here,
1282 // unfortunately, this fails within a wizard
1283 // startWizard = new MenuItem(men, SWT.PUSH);
1284 // startWizard.setText("Start the Import wizard...");
1285 // startWizard.addSelectionListener(new SelectionAdapter() {
1287 // @Override
1288 // public void widgetSelected(SelectionEvent e) {
1290 // IHandlerService handlerService = (IHandlerService) getSite()
1291 // .getWorkbenchWindow().getWorkbench().getService(
1292 // IHandlerService.class);
1294 // try {
1295 // handlerService.executeCommand("org.eclipse.ui.file.import", //$NON-NLS-1$
1296 // null);
1297 // } catch (ExecutionException e1) {
1298 // Activator.handleError(e1.getMessage(), e1, true);
1299 // } catch (NotDefinedException e1) {
1300 // Activator.handleError(e1.getMessage(), e1, true);
1301 // } catch (NotEnabledException e1) {
1302 // Activator.handleError(e1.getMessage(), e1, true);
1303 // } catch (NotHandledException e1) {
1304 // Activator.handleError(e1.getMessage(), e1, true);
1305 // }
1306 // }
1308 // });
1311 private void addActionsToToolbar() {
1312 importAction = new Action(UIText.RepositoriesView_Import_Button) {
1314 @Override
1315 public void run() {
1316 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1317 new GitCloneWizard());
1318 if (dlg.open() == Window.OK)
1319 scheduleRefresh();
1322 importAction.setToolTipText(UIText.RepositoriesView_Clone_Tooltip);
1324 importAction.setImageDescriptor(UIIcons.IMPORT);
1326 getViewSite().getActionBars().getToolBarManager().add(importAction);
1328 addAction = new Action(UIText.RepositoriesView_Add_Button) {
1330 @Override
1331 public void run() {
1332 RepositorySearchDialog sd = new RepositorySearchDialog(
1333 getSite().getShell(), getDirs());
1334 if (sd.open() == Window.OK) {
1335 Set<String> dirs = new HashSet<String>();
1336 dirs.addAll(getDirs());
1337 if (dirs.addAll(sd.getDirectories()))
1338 saveDirs(dirs);
1339 scheduleRefresh();
1344 addAction.setToolTipText(UIText.RepositoriesView_AddRepository_Tooltip);
1346 addAction.setImageDescriptor(UIIcons.NEW_REPOSITORY);
1348 getViewSite().getActionBars().getToolBarManager().add(addAction);
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);
1376 linkWithSelectionAction.setImageDescriptor(UIIcons.ELCL16_SYNCED);
1378 linkWithSelectionAction.setChecked(getPrefs().getBoolean(PREFS_SYNCED,
1379 false));
1381 getViewSite().getActionBars().getToolBarManager().add(
1382 linkWithSelectionAction);
1384 refreshAction = new Action(UIText.RepositoriesView_Refresh_Button) {
1386 @Override
1387 public void run() {
1388 scheduleRefresh();
1392 refreshAction.setImageDescriptor(UIIcons.ELCL16_REFRESH);
1394 getViewSite().getActionBars().getToolBarManager().add(refreshAction);
1396 IAction collapseAllAction = new Action(
1397 UIText.RepositoriesView_CollapseAllMenu) {
1399 @Override
1400 public void run() {
1401 tv.collapseAll();
1405 // copy and paste are global actions; we just implement them
1406 // and register them with the global action handler
1407 // we enable/disable them upon tree selection changes
1409 copyAction = new Action("") { //$NON-NLS-1$
1411 @Override
1412 public void run() {
1413 // for REPO, WORKINGDIR, FILE, FOLDER: copy directory
1414 IStructuredSelection sel = (IStructuredSelection) tv
1415 .getSelection();
1416 if (sel.size() == 1) {
1417 RepositoryTreeNode node = (RepositoryTreeNode) sel
1418 .getFirstElement();
1419 String dir = null;
1420 if (node.getType() == RepositoryTreeNodeType.REPO) {
1421 dir = node.getRepository().getDirectory().getPath();
1422 } else if (node.getType() == RepositoryTreeNodeType.FILE
1423 || node.getType() == RepositoryTreeNodeType.FOLDER) {
1424 dir = ((File) node.getObject()).getPath();
1425 } else if (node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1426 dir = node.getRepository().getWorkDir().getPath();
1428 if (dir != null) {
1429 Clipboard clip = null;
1430 try {
1431 clip = new Clipboard(getSite().getShell()
1432 .getDisplay());
1433 clip
1434 .setContents(new Object[] { dir },
1435 new Transfer[] { TextTransfer
1436 .getInstance() });
1437 } finally {
1438 if (clip != null)
1439 // we must dispose ourselves
1440 clip.dispose();
1447 copyAction.setEnabled(false);
1449 getViewSite().getActionBars().setGlobalActionHandler(
1450 ActionFactory.COPY.getId(), copyAction);
1452 pasteAction = new Action("") { //$NON-NLS-1$
1454 @Override
1455 public void run() {
1456 // we check if the pasted content is a directory
1457 // repository location and try to add this
1458 String errorMessage = null;
1460 Clipboard clip = null;
1461 try {
1462 clip = new Clipboard(getSite().getShell().getDisplay());
1463 String content = (String) clip.getContents(TextTransfer
1464 .getInstance());
1465 if (content == null) {
1466 errorMessage = UIText.RepositoriesView_NothingToPasteMessage;
1467 return;
1470 File file = new File(content);
1471 if (!file.exists() || !file.isDirectory()) {
1472 errorMessage = UIText.RepositoriesView_ClipboardContentNotDirectoryMessage;
1473 return;
1476 if (!RepositoryCache.FileKey.isGitRepository(file)) {
1477 errorMessage = NLS
1478 .bind(
1479 UIText.RepositoriesView_ClipboardContentNoGitRepoMessage,
1480 content);
1481 return;
1484 if (addDir(file))
1485 scheduleRefresh();
1486 else
1487 errorMessage = NLS.bind(
1488 UIText.RepositoriesView_PasteRepoAlreadyThere,
1489 content);
1490 } finally {
1491 if (clip != null)
1492 // we must dispose ourselves
1493 clip.dispose();
1494 if (errorMessage != null)
1495 // TODO String ext
1496 MessageDialog.openWarning(getSite().getShell(),
1497 UIText.RepositoriesView_PasteFailureTitle,
1498 errorMessage);
1504 collapseAllAction.setImageDescriptor(UIIcons.COLLAPSEALL);
1506 getViewSite().getActionBars().getToolBarManager()
1507 .add(collapseAllAction);
1509 getViewSite().getActionBars().setGlobalActionHandler(
1510 ActionFactory.PASTE.getId(), pasteAction);
1515 * @return the preferences
1517 protected static IEclipsePreferences getPrefs() {
1518 return new InstanceScope().getNode(Activator.getPluginId());
1521 @Override
1522 public void dispose() {
1523 // make sure to cancel the refresh job
1524 if (this.scheduledJob != null) {
1525 this.scheduledJob.cancel();
1526 this.scheduledJob = null;
1528 super.dispose();
1532 * Schedules a refreh
1534 public void scheduleRefresh() {
1536 Job job = new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1538 @Override
1539 protected IStatus run(IProgressMonitor monitor) {
1541 final List<RepositoryTreeNode<Repository>> input;
1542 try {
1543 input = getRepositoriesFromDirs(monitor);
1544 } catch (InterruptedException e) {
1545 return new Status(IStatus.ERROR, Activator.getPluginId(), e
1546 .getMessage(), e);
1549 boolean needsNewInput = tv.getInput() == null;
1550 List oldInput = (List) tv.getInput();
1551 if (!needsNewInput)
1552 needsNewInput = oldInput.size() != input.size();
1554 if (!needsNewInput) {
1555 for (int i = 0; i < input.size(); i++) {
1556 needsNewInput = !input.get(i).equals(oldInput.get(i));
1557 if (needsNewInput)
1558 break;
1562 final boolean updateInput = needsNewInput;
1564 Display.getDefault().syncExec(new Runnable() {
1566 public void run() {
1567 // keep expansion state and selection so that we can
1568 // restore the tree
1569 // after update
1570 Object[] expanded = tv.getExpandedElements();
1571 IStructuredSelection sel = (IStructuredSelection) tv
1572 .getSelection();
1573 if (updateInput)
1574 tv.setInput(input);
1575 else
1576 tv.refresh();
1577 tv.setExpandedElements(expanded);
1579 Object selected = sel.getFirstElement();
1580 if (selected != null)
1581 tv.reveal(selected);
1583 IViewPart part = PlatformUI.getWorkbench()
1584 .getActiveWorkbenchWindow().getActivePage()
1585 .findView(IPageLayout.ID_PROP_SHEET);
1586 if (part != null) {
1587 PropertySheet sheet = (PropertySheet) part;
1588 PropertySheetPage page = (PropertySheetPage) sheet
1589 .getCurrentPage();
1590 page.refresh();
1595 return new Status(IStatus.OK, Activator.getPluginId(), ""); //$NON-NLS-1$
1600 job.setSystem(true);
1602 IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService) getSite()
1603 .getService(IWorkbenchSiteProgressService.class);
1605 service.schedule(job);
1607 scheduledJob = job;
1612 * Adds a directory to the list if it is not already there
1614 * @param file
1615 * @return see {@link Collection#add(Object)}
1617 public static boolean addDir(File file) {
1619 String dirString;
1620 try {
1621 dirString = file.getCanonicalPath();
1622 } catch (IOException e) {
1623 dirString = file.getAbsolutePath();
1626 List<String> dirStrings = getDirs();
1627 if (dirStrings.contains(dirString)) {
1628 return false;
1629 } else {
1630 Set<String> dirs = new HashSet<String>();
1631 dirs.addAll(dirStrings);
1632 dirs.add(dirString);
1633 saveDirs(dirs);
1634 return true;
1639 * Converts the directories as configured for this view into a list of
1640 * {@link Repository} objects suitable for the tree content provider
1641 * <p>
1642 * TODO move to some utility class
1644 * @param monitor
1645 * @return a list of nodes
1646 * @throws InterruptedException
1648 public static List<RepositoryTreeNode<Repository>> getRepositoriesFromDirs(
1649 IProgressMonitor monitor) throws InterruptedException {
1651 List<String> gitDirStrings = getDirs();
1652 List<RepositoryTreeNode<Repository>> input = new ArrayList<RepositoryTreeNode<Repository>>();
1654 for (String dirString : gitDirStrings) {
1655 if (monitor != null && monitor.isCanceled()) {
1656 throw new InterruptedException(
1657 UIText.RepositoriesView_ActionCanceled_Message);
1659 try {
1660 File dir = new File(dirString);
1661 if (dir.exists() && dir.isDirectory()) {
1662 Repository repo = new Repository(dir);
1663 RepositoryTreeNode<Repository> node = new RepositoryTreeNode<Repository>(
1664 null, RepositoryTreeNodeType.REPO, repo, repo);
1665 input.add(node);
1667 } catch (IOException e) {
1668 IStatus error = new Status(IStatus.ERROR, Activator
1669 .getPluginId(), e.getMessage(), e);
1670 Activator.getDefault().getLog().log(error);
1673 Collections.sort(input);
1674 return input;
1677 private static void saveDirs(Set<String> gitDirStrings) {
1678 StringBuilder sb = new StringBuilder();
1679 for (String gitDirString : gitDirStrings) {
1680 sb.append(gitDirString);
1681 sb.append(File.pathSeparatorChar);
1684 IEclipsePreferences prefs = getPrefs();
1685 prefs.put(PREFS_DIRECTORIES, sb.toString());
1686 try {
1687 prefs.flush();
1688 } catch (BackingStoreException e) {
1689 IStatus error = new Status(IStatus.ERROR, Activator.getPluginId(),
1690 e.getMessage(), e);
1691 Activator.getDefault().getLog().log(error);
1695 @Override
1696 public void setFocus() {
1697 tv.getTree().setFocus();
1700 @SuppressWarnings("boxing")
1701 private boolean confirmProjectDeletion(List<IProject> projectsToDelete) {
1702 boolean confirmed;
1703 confirmed = MessageDialog
1704 .openConfirm(
1705 getSite().getShell(),
1706 UIText.RepositoriesView_ConfirmProjectDeletion_WindowTitle,
1708 .bind(
1709 UIText.RepositoriesView_ConfirmProjectDeletion_Question,
1710 projectsToDelete.size()));
1711 return confirmed;
1714 public void addSelectionChangedListener(ISelectionChangedListener listener) {
1715 selectionListeners.add(listener);
1718 public ISelection getSelection() {
1719 return currentSelection;
1722 public void removeSelectionChangedListener(
1723 ISelectionChangedListener listener) {
1724 selectionListeners.remove(listener);
1728 public void setSelection(ISelection selection) {
1729 currentSelection = selection;
1730 for (ISelectionChangedListener listener : selectionListeners) {
1731 listener.selectionChanged(new SelectionChangedEvent(
1732 RepositoriesView.this, selection));
1737 * Opens the tree and marks the folder to which a project is pointing
1739 * @param resource
1740 * TODO exceptions?
1742 @SuppressWarnings("unchecked")
1743 public void showResource(final IResource resource) {
1744 IProject project = resource.getProject();
1745 RepositoryMapping mapping = RepositoryMapping.getMapping(project);
1746 if (mapping == null)
1747 return;
1749 if (addDir(mapping.getRepository().getDirectory())) {
1750 scheduleRefresh();
1753 boolean doSetSelection = false;
1755 if (this.scheduledJob != null) {
1756 int state = this.scheduledJob.getState();
1757 if (state == Job.WAITING || state == Job.RUNNING) {
1758 this.scheduledJob.addJobChangeListener(new JobChangeAdapter() {
1760 @Override
1761 public void done(IJobChangeEvent event) {
1762 showResource(resource);
1765 } else {
1766 doSetSelection = true;
1770 if (doSetSelection) {
1771 RepositoriesViewContentProvider cp = (RepositoriesViewContentProvider) tv
1772 .getContentProvider();
1773 RepositoryTreeNode currentNode = null;
1774 Object[] repos = cp.getElements(tv.getInput());
1775 for (Object repo : repos) {
1776 RepositoryTreeNode node = (RepositoryTreeNode) repo;
1777 // TODO equals implementation of Repository?
1778 if (mapping.getRepository().getDirectory().equals(
1779 ((Repository) node.getObject()).getDirectory())) {
1780 for (Object child : cp.getChildren(node)) {
1781 RepositoryTreeNode childNode = (RepositoryTreeNode) child;
1782 if (childNode.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1783 currentNode = childNode;
1784 break;
1787 break;
1791 IPath relPath = new Path(mapping.getRepoRelativePath(resource));
1793 for (String segment : relPath.segments()) {
1794 for (Object child : cp.getChildren(currentNode)) {
1795 RepositoryTreeNode<File> childNode = (RepositoryTreeNode<File>) child;
1796 if (childNode.getObject().getName().equals(segment)) {
1797 currentNode = childNode;
1798 break;
1803 final RepositoryTreeNode selNode = currentNode;
1805 Display.getDefault().asyncExec(new Runnable() {
1807 public void run() {
1808 tv.setSelection(new StructuredSelection(selNode), true);
1816 public boolean show(ShowInContext context) {
1817 ISelection selection = context.getSelection();
1818 if (selection instanceof IStructuredSelection) {
1819 IStructuredSelection ss = (IStructuredSelection) selection;
1820 if (ss.size() == 1) {
1821 Object element = ss.getFirstElement();
1822 if (element instanceof IAdaptable) {
1823 IResource resource = (IResource) ((IAdaptable) element)
1824 .getAdapter(IResource.class);
1825 if (resource != null) {
1826 showResource(resource);
1827 return true;
1832 return false;