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