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
.GitImportProjectsWizard
;
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
.Wizard
;
71 import org
.eclipse
.jface
.wizard
.WizardDialog
;
72 import org
.eclipse
.jgit
.lib
.Ref
;
73 import org
.eclipse
.jgit
.lib
.Repository
;
74 import org
.eclipse
.jgit
.lib
.RepositoryConfig
;
75 import org
.eclipse
.jgit
.transport
.RemoteConfig
;
76 import org
.eclipse
.osgi
.util
.NLS
;
77 import org
.eclipse
.swt
.SWT
;
78 import org
.eclipse
.swt
.events
.MenuDetectEvent
;
79 import org
.eclipse
.swt
.events
.MenuDetectListener
;
80 import org
.eclipse
.swt
.events
.SelectionAdapter
;
81 import org
.eclipse
.swt
.events
.SelectionEvent
;
82 import org
.eclipse
.swt
.graphics
.Point
;
83 import org
.eclipse
.swt
.widgets
.Composite
;
84 import org
.eclipse
.swt
.widgets
.Display
;
85 import org
.eclipse
.swt
.widgets
.Menu
;
86 import org
.eclipse
.swt
.widgets
.MenuItem
;
87 import org
.eclipse
.swt
.widgets
.TreeItem
;
88 import org
.eclipse
.ui
.IEditorInput
;
89 import org
.eclipse
.ui
.IEditorPart
;
90 import org
.eclipse
.ui
.IFileEditorInput
;
91 import org
.eclipse
.ui
.IPageLayout
;
92 import org
.eclipse
.ui
.ISelectionListener
;
93 import org
.eclipse
.ui
.ISelectionService
;
94 import org
.eclipse
.ui
.IViewPart
;
95 import org
.eclipse
.ui
.IWorkbenchPart
;
96 import org
.eclipse
.ui
.PartInitException
;
97 import org
.eclipse
.ui
.PlatformUI
;
98 import org
.eclipse
.ui
.editors
.text
.EditorsUI
;
99 import org
.eclipse
.ui
.ide
.FileStoreEditorInput
;
100 import org
.eclipse
.ui
.ide
.IDE
;
101 import org
.eclipse
.ui
.part
.ViewPart
;
102 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
103 import org
.eclipse
.ui
.views
.properties
.IPropertySheetPage
;
104 import org
.eclipse
.ui
.views
.properties
.PropertySheet
;
105 import org
.eclipse
.ui
.views
.properties
.PropertySheetPage
;
106 import org
.osgi
.service
.prefs
.BackingStoreException
;
110 * The Git Repositories view.
112 * This keeps track of a bunch of local directory names each of which represent
113 * a Git Repository. This list is stored in some Preferences object and used to
114 * build the tree in the view.
116 * Implements {@link ISelectionProvider} in order to integrate with the
120 * <li>Clarification whether to show projects, perhaps configurable switch</li>
123 public class RepositoriesView
extends ViewPart
implements ISelectionProvider
{
126 public static final String VIEW_ID
= "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
128 // TODO central constants? RemoteConfig ones are private
129 static final String REMOTE
= "remote"; //$NON-NLS-1$
131 static final String URL
= "url"; //$NON-NLS-1$
133 static final String PUSHURL
= "pushurl"; //$NON-NLS-1$
135 static final String FETCH
= "fetch"; //$NON-NLS-1$
137 static final String PUSH
= "push"; //$NON-NLS-1$
139 private static final String PREFS_DIRECTORIES
= "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
141 private static final String PREFS_SYNCED
= "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
143 private final List
<ISelectionChangedListener
> selectionListeners
= new ArrayList
<ISelectionChangedListener
>();
145 private ISelection currentSelection
= new StructuredSelection();
147 private Job scheduledJob
;
149 private TreeViewer tv
;
151 private IAction importAction
;
153 private IAction addAction
;
155 private IAction refreshAction
;
157 private IAction linkWithSelectionAction
;
159 private static List
<String
> getDirs() {
160 List
<String
> resultStrings
= new ArrayList
<String
>();
161 String dirs
= getPrefs().get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
162 if (dirs
!= null && dirs
.length() > 0) {
163 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
164 while (tok
.hasMoreTokens()) {
165 String dirName
= tok
.nextToken();
166 File testFile
= new File(dirName
);
167 if (testFile
.exists()) {
168 resultStrings
.add(dirName
);
172 Collections
.sort(resultStrings
);
173 return resultStrings
;
176 private static void removeDir(File file
) {
180 dir
= file
.getCanonicalPath();
181 } catch (IOException e1
) {
182 dir
= file
.getAbsolutePath();
185 IEclipsePreferences prefs
= getPrefs();
187 TreeSet
<String
> resultStrings
= new TreeSet
<String
>();
188 String dirs
= prefs
.get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
189 if (dirs
!= null && dirs
.length() > 0) {
190 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
191 while (tok
.hasMoreTokens()) {
192 String dirName
= tok
.nextToken();
193 File testFile
= new File(dirName
);
194 if (testFile
.exists()) {
196 resultStrings
.add(testFile
.getCanonicalPath());
197 } catch (IOException e
) {
198 resultStrings
.add(testFile
.getAbsolutePath());
204 if (resultStrings
.remove(dir
)) {
205 StringBuilder sb
= new StringBuilder();
206 for (String gitDirString
: resultStrings
) {
207 sb
.append(gitDirString
);
208 sb
.append(File
.pathSeparatorChar
);
211 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
214 } catch (BackingStoreException e
) {
215 IStatus error
= new Status(IStatus
.ERROR
, Activator
216 .getPluginId(), e
.getMessage(), e
);
217 Activator
.getDefault().getLog().log(error
);
224 public Object
getAdapter(Class adapter
) {
225 // integrate with Properties view
226 if (adapter
== IPropertySheetPage
.class) {
227 PropertySheetPage page
= new PropertySheetPage();
229 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
234 return super.getAdapter(adapter
);
238 public void createPartControl(Composite parent
) {
239 tv
= new TreeViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
240 tv
.setContentProvider(new RepositoriesViewContentProvider());
241 // the label provider registers itself
242 new RepositoriesViewLabelProvider(tv
);
244 getSite().setSelectionProvider(this);
246 tv
.addSelectionChangedListener(new ISelectionChangedListener() {
248 public void selectionChanged(SelectionChangedEvent event
) {
250 IStructuredSelection ssel
= (IStructuredSelection
) event
252 if (ssel
.size() == 1) {
253 setSelection(new StructuredSelection(ssel
.getFirstElement()));
255 setSelection(new StructuredSelection());
260 tv
.addOpenListener(new IOpenListener() {
261 public void open(OpenEvent event
) {
262 IStructuredSelection selection
= (IStructuredSelection
) event
264 if (selection
.isEmpty()) {
265 // nothing selected, ignore
269 Object element
= selection
.getFirstElement();
270 ITreeContentProvider contentProvider
= (ITreeContentProvider
) tv
271 .getContentProvider();
272 if (contentProvider
.hasChildren(element
)) {
273 // this element has children, expand/collapse it
274 tv
.setExpandedState(element
, !tv
.getExpandedState(element
));
276 Object
[] selectionArray
= selection
.toArray();
277 for (Object selectedElement
: selectionArray
) {
278 RepositoryTreeNode node
= (RepositoryTreeNode
) selectedElement
;
279 // if any of the selected elements are not files, ignore
281 if (node
.getType() != RepositoryTreeNodeType
.FILE
) {
286 // open the files the user has selected
287 for (Object selectedElement
: selectionArray
) {
288 RepositoryTreeNode node
= (RepositoryTreeNode
) selectedElement
;
289 openFile((File
) node
.getObject());
297 addActionsToToolbar();
301 ISelectionService srv
= (ISelectionService
) getSite().getService(
302 ISelectionService
.class);
303 srv
.addPostSelectionListener(new ISelectionListener() {
305 public void selectionChanged(IWorkbenchPart part
,
306 ISelection selection
) {
308 // if the "link with selection" toggle is off, we're done
309 if (linkWithSelectionAction
== null
310 || !linkWithSelectionAction
.isChecked())
313 // this may happen if we switch between editors
314 if (part
instanceof IEditorPart
) {
315 IEditorInput input
= ((IEditorPart
) part
).getEditorInput();
316 if (input
instanceof IFileEditorInput
)
317 reactOnSelection(new StructuredSelection(
318 ((IFileEditorInput
) input
).getFile()));
321 reactOnSelection(selection
);
328 private void reactOnSelection(ISelection selection
) {
329 if (selection
instanceof StructuredSelection
) {
330 StructuredSelection ssel
= (StructuredSelection
) selection
;
331 if (ssel
.size() != 1)
333 if (ssel
.getFirstElement() instanceof IResource
) {
334 showResource((IResource
) ssel
.getFirstElement());
336 if (ssel
.getFirstElement() instanceof IAdaptable
) {
337 IResource adapted
= (IResource
) ((IAdaptable
) ssel
338 .getFirstElement()).getAdapter(IResource
.class);
340 showResource(adapted
);
345 private void addContextMenu() {
346 tv
.getTree().addMenuDetectListener(new MenuDetectListener() {
348 public void menuDetected(MenuDetectEvent e
) {
350 tv
.getTree().setMenu(null);
351 Menu men
= new Menu(tv
.getTree());
353 TreeItem testItem
= tv
.getTree().getItem(
354 tv
.getTree().toControl(new Point(e
.x
, e
.y
)));
355 if (testItem
== null) {
356 addMenuItemsForPanel(men
);
358 addMenuItemsForTreeSelection(men
);
361 tv
.getTree().setMenu(men
);
366 private void addMenuItemsForPanel(Menu men
) {
368 MenuItem importItem
= new MenuItem(men
, SWT
.PUSH
);
369 importItem
.setText(UIText
.RepositoriesView_ImportRepository_MenuItem
);
370 importItem
.addSelectionListener(new SelectionAdapter() {
373 public void widgetSelected(SelectionEvent e
) {
379 MenuItem addItem
= new MenuItem(men
, SWT
.PUSH
);
380 addItem
.setText(UIText
.RepositoriesView_AddRepository_MenuItem
);
381 addItem
.addSelectionListener(new SelectionAdapter() {
384 public void widgetSelected(SelectionEvent e
) {
390 MenuItem refreshItem
= new MenuItem(men
, SWT
.PUSH
);
391 refreshItem
.setText(refreshAction
.getText());
392 refreshItem
.addSelectionListener(new SelectionAdapter() {
395 public void widgetSelected(SelectionEvent e
) {
403 @SuppressWarnings("unchecked")
404 private void addMenuItemsForTreeSelection(Menu men
) {
406 final IStructuredSelection sel
= (IStructuredSelection
) tv
409 boolean importableProjectsOnly
= true;
411 for (Object node
: sel
.toArray()) {
412 RepositoryTreeNode tnode
= (RepositoryTreeNode
) node
;
413 importableProjectsOnly
= tnode
.getType() == RepositoryTreeNodeType
.PROJ
;
414 if (!importableProjectsOnly
)
418 if (importableProjectsOnly
) {
419 MenuItem sync
= new MenuItem(men
, SWT
.PUSH
);
420 sync
.setText(UIText
.RepositoriesView_ImportProject_MenuItem
);
422 sync
.addSelectionListener(new SelectionAdapter() {
425 public void widgetSelected(SelectionEvent e
) {
427 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
429 public void run(IProgressMonitor monitor
)
430 throws CoreException
{
432 for (Object selected
: sel
.toArray()) {
433 RepositoryTreeNode
<File
> projectNode
= (RepositoryTreeNode
<File
>) selected
;
434 File file
= projectNode
.getObject();
436 IProjectDescription pd
= ResourcesPlugin
437 .getWorkspace().newProjectDescription(
439 IPath locationPath
= new Path(file
442 pd
.setLocation(locationPath
);
444 ResourcesPlugin
.getWorkspace().getRoot()
445 .getProject(pd
.getName()).create(pd
,
447 IProject project
= ResourcesPlugin
448 .getWorkspace().getRoot().getProject(
450 project
.open(monitor
);
452 File gitDir
= projectNode
.getRepository()
455 ConnectProviderOperation connectProviderOperation
= new ConnectProviderOperation(
457 connectProviderOperation
458 .run(new SubProgressMonitor(monitor
, 20));
467 ResourcesPlugin
.getWorkspace().run(wsr
,
468 ResourcesPlugin
.getWorkspace().getRoot(),
469 IWorkspace
.AVOID_UPDATE
,
470 new NullProgressMonitor());
473 } catch (CoreException e1
) {
474 Activator
.logError(e1
.getMessage(), e1
);
482 // from here on, we only deal with single selection
486 final RepositoryTreeNode node
= (RepositoryTreeNode
) sel
489 // for Refs (branches): checkout
490 if (node
.getType() == RepositoryTreeNodeType
.REF
) {
492 final Ref ref
= (Ref
) node
.getObject();
494 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
495 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
496 checkout
.addSelectionListener(new SelectionAdapter() {
499 public void widgetSelected(SelectionEvent e
) {
501 final String refName
= ref
.getLeaf().getName();
503 Job job
= new Job(NLS
.bind(UIText
.RepositoriesView_CheckingOutMessage
, refName
)) {
506 protected IStatus
run(IProgressMonitor monitor
) {
508 Repository repo
= node
.getRepository();
510 final BranchOperation op
= new BranchOperation(
512 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
514 public void run(IProgressMonitor myMonitor
)
515 throws CoreException
{
521 ResourcesPlugin
.getWorkspace().run(
523 ResourcesPlugin
.getWorkspace()
525 IWorkspace
.AVOID_UPDATE
,
528 } catch (CoreException e1
) {
529 return new Status(IStatus
.ERROR
, Activator
530 .getPluginId(), e1
.getMessage(), e1
);
532 return Status
.OK_STATUS
;
544 // for Repository: import existing projects, remove, (delete), open
546 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
548 final Repository repo
= (Repository
) node
.getObject();
550 // TODO "import existing plug-in" menu item
552 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
553 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
554 remove
.addSelectionListener(new SelectionAdapter() {
557 public void widgetSelected(SelectionEvent e
) {
559 List
<IProject
> projectsToDelete
= new ArrayList
<IProject
>();
560 File workDir
= repo
.getWorkDir();
561 final IPath wdPath
= new Path(workDir
.getAbsolutePath());
562 for (IProject prj
: ResourcesPlugin
.getWorkspace()
563 .getRoot().getProjects()) {
564 if (wdPath
.isPrefixOf(prj
.getLocation())) {
565 projectsToDelete
.add(prj
);
569 if (!projectsToDelete
.isEmpty()) {
571 confirmed
= confirmProjectDeletion(projectsToDelete
);
577 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
579 public void run(IProgressMonitor monitor
)
580 throws CoreException
{
582 for (IProject prj
: ResourcesPlugin
.getWorkspace()
583 .getRoot().getProjects()) {
584 if (wdPath
.isPrefixOf(prj
.getLocation())) {
585 prj
.delete(false, false, monitor
);
589 removeDir(repo
.getDirectory());
595 ResourcesPlugin
.getWorkspace().run(wsr
,
596 ResourcesPlugin
.getWorkspace().getRoot(),
597 IWorkspace
.AVOID_UPDATE
,
598 new NullProgressMonitor());
599 } catch (CoreException e1
) {
600 Activator
.logError(e1
.getMessage(), e1
);
607 // TODO delete does not work because of file locks on .pack-files
608 // Shawn Pearce has added the following thoughts:
610 // Hmm. We probably can't active detect file locks on pack files on
612 // It would be nice if we could support a delete, but only if the
614 // reasonably believed to be not-in-use right now.
616 // Within EGit you might be able to check GitProjectData and its
617 // repositoryCache to
618 // see if the repository is open by this workspace. If it is, then
619 // we know we shouldn't
622 // Some coding might look like this:
624 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
625 // deleteRepo.setText("Delete");
626 // deleteRepo.addSelectionListener(new SelectionAdapter() {
629 // public void widgetSelected(SelectionEvent e) {
631 // boolean confirmed = MessageDialog.openConfirm(getSite()
632 // .getShell(), "Confirm",
633 // "This will delete the repository, continue?");
638 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
640 // public void run(IProgressMonitor monitor)
641 // throws CoreException {
642 // File workDir = repos.get(0).getRepository()
645 // File gitDir = repos.get(0).getRepository()
648 // IPath wdPath = new Path(workDir.getAbsolutePath());
649 // for (IProject prj : ResourcesPlugin.getWorkspace()
650 // .getRoot().getProjects()) {
651 // if (wdPath.isPrefixOf(prj.getLocation())) {
652 // prj.delete(false, false, monitor);
656 // repos.get(0).getRepository().close();
658 // boolean deleted = deleteRecursively(gitDir, monitor);
660 // MessageDialog.openError(getSite().getShell(),
662 // "Could not delete Git Repository");
665 // deleted = deleteRecursively(workDir, monitor);
668 // .openError(getSite().getShell(),
670 // "Could not delete Git Working Directory");
673 // scheduleRefresh();
676 // private boolean deleteRecursively(File fileToDelete,
677 // IProgressMonitor monitor) {
678 // if (fileToDelete.isDirectory()) {
679 // for (File file : fileToDelete.listFiles()) {
680 // if (!deleteRecursively(file, monitor)) {
685 // monitor.setTaskName(fileToDelete.getAbsolutePath());
686 // boolean deleted = fileToDelete.delete();
688 // System.err.println("Could not delete "
689 // + fileToDelete.getAbsolutePath());
696 // ResourcesPlugin.getWorkspace().run(wsr,
697 // ResourcesPlugin.getWorkspace().getRoot(),
698 // IWorkspace.AVOID_UPDATE,
699 // new NullProgressMonitor());
700 // } catch (CoreException e1) {
701 // // TODO Exception handling
702 // e1.printStackTrace();
709 new MenuItem(men
, SWT
.SEPARATOR
);
711 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
712 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
713 openPropsView
.addSelectionListener(new SelectionAdapter() {
716 public void widgetSelected(SelectionEvent e
) {
718 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
719 .getActivePage().showView(
720 IPageLayout
.ID_PROP_SHEET
);
721 } catch (PartInitException e1
) {
729 if (node
.getType() == RepositoryTreeNodeType
.REMOTES
) {
731 MenuItem remoteConfig
= new MenuItem(men
, SWT
.PUSH
);
732 remoteConfig
.setText(UIText
.RepositoriesView_NewRemoteMenu
);
733 remoteConfig
.addSelectionListener(new SelectionAdapter() {
736 public void widgetSelected(SelectionEvent e
) {
738 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
739 new NewRemoteWizard(node
.getRepository()));
740 if (dlg
.open() == Window
.OK
)
748 if (node
.getType() == RepositoryTreeNodeType
.REMOTE
) {
750 final String configName
= (String
) node
.getObject();
752 RemoteConfig rconfig
;
754 rconfig
= new RemoteConfig(node
.getRepository().getConfig(),
756 } catch (URISyntaxException e2
) {
757 // TODO Exception handling
761 boolean fetchExists
= rconfig
!= null
762 && !rconfig
.getURIs().isEmpty();
763 boolean pushExists
= rconfig
!= null
764 && !rconfig
.getPushURIs().isEmpty();
766 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
769 .setText(UIText
.RepositoriesView_ConfigureFetchMenu
);
772 .setText(UIText
.RepositoriesView_CreateFetch_menu
);
773 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
776 public void widgetSelected(SelectionEvent e
) {
778 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
779 new ConfigureRemoteWizard(node
.getRepository(),
781 if (dlg
.open() == Window
.OK
)
789 MenuItem deleteFetch
= new MenuItem(men
, SWT
.PUSH
);
790 deleteFetch
.setText(UIText
.RepositoriesView_RemoveFetch_menu
);
791 deleteFetch
.addSelectionListener(new SelectionAdapter() {
794 public void widgetSelected(SelectionEvent e
) {
795 RepositoryConfig config
= node
.getRepository()
797 config
.unset("remote", configName
, "url"); //$NON-NLS-1$ //$NON-NLS-2$
798 config
.unset("remote", configName
, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
802 } catch (IOException e1
) {
803 MessageDialog
.openError(getSite().getShell(),
804 UIText
.RepositoriesView_ErrorHeader
, e1
812 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
815 .setText(UIText
.RepositoriesView_ConfigurePushMenu
);
818 .setText(UIText
.RepositoriesView_CreatePush_menu
);
819 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
822 public void widgetSelected(SelectionEvent e
) {
824 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
825 new ConfigureRemoteWizard(node
.getRepository(),
827 if (dlg
.open() == Window
.OK
)
834 MenuItem deleteFetch
= new MenuItem(men
, SWT
.PUSH
);
835 deleteFetch
.setText(UIText
.RepositoriesView_RemovePush_menu
);
836 deleteFetch
.addSelectionListener(new SelectionAdapter() {
839 public void widgetSelected(SelectionEvent e
) {
840 RepositoryConfig config
= node
.getRepository()
842 config
.unset("remote", configName
, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
843 config
.unset("remote", configName
, "push"); //$NON-NLS-1$ //$NON-NLS-2$
847 } catch (IOException e1
) {
848 MessageDialog
.openError(getSite().getShell(),
849 UIText
.RepositoriesView_ErrorHeader
, e1
857 new MenuItem(men
, SWT
.SEPARATOR
);
859 MenuItem removeRemote
= new MenuItem(men
, SWT
.PUSH
);
860 removeRemote
.setText(UIText
.RepositoriesView_RemoveRemoteMenu
);
861 removeRemote
.addSelectionListener(new SelectionAdapter() {
864 public void widgetSelected(SelectionEvent e
) {
866 boolean ok
= MessageDialog
868 getSite().getShell(),
869 UIText
.RepositoriesView_ConfirmDeleteRemoteHeader
,
872 UIText
.RepositoriesView_ConfirmDeleteRemoteMessage
,
875 RepositoryConfig config
= node
.getRepository()
877 config
.unsetSection(REMOTE
, configName
);
881 } catch (IOException e1
) {
882 MessageDialog
.openError(getSite().getShell(),
883 UIText
.RepositoriesView_ErrorHeader
, e1
892 new MenuItem(men
, SWT
.SEPARATOR
);
894 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
895 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
896 openPropsView
.addSelectionListener(new SelectionAdapter() {
899 public void widgetSelected(SelectionEvent e
) {
901 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
902 .getActivePage().showView(
903 IPageLayout
.ID_PROP_SHEET
);
904 } catch (PartInitException e1
) {
912 if (node
.getType() == RepositoryTreeNodeType
.FILE
) {
914 final File file
= (File
) node
.getObject();
916 MenuItem openInTextEditor
= new MenuItem(men
, SWT
.PUSH
);
918 .setText(UIText
.RepositoriesView_OpenInTextEditor_menu
);
919 openInTextEditor
.addSelectionListener(new SelectionAdapter() {
922 public void widgetSelected(SelectionEvent e
) {
929 if (node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
930 String path
= ((File
) node
.getObject()).getAbsolutePath();
931 createImportProjectItem(men
, node
.getRepository(), path
);
934 if (node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
935 String path
= node
.getRepository().getWorkDir().getAbsolutePath();
936 createImportProjectItem(men
, node
.getRepository(), path
);
941 private void openFile(File file
) {
942 IFileStore store
= EFS
.getLocalFileSystem().getStore(
943 new Path(file
.getAbsolutePath()));
945 // TODO do we need a read-only editor here?
946 IDE
.openEditor(getSite().getPage(),
947 new FileStoreEditorInput(store
),
948 EditorsUI
.DEFAULT_TEXT_EDITOR_ID
);
949 } catch (PartInitException e
) {
950 Activator
.handleError(UIText
.RepositoriesView_Error_WindowTitle
, e
, true);
954 private void createImportProjectItem(Menu men
, final Repository repo
,
956 MenuItem importProjects
;
957 importProjects
= new MenuItem(men
, SWT
.PUSH
);
959 .setText(UIText
.RepositoriesView_ImportExistingProjects_MenuItem
);
960 importProjects
.addSelectionListener(new SelectionAdapter() {
963 public void widgetSelected(SelectionEvent e
) {
964 // Instead of the generic ExternalProjectImportWizard
965 // from the org.eclipse.ui.ide plug-in, we use our
966 // own wizard (the generic one does not allow to set the
967 // path in 3.4; in addition, we have added project filtering
969 Wizard wiz
= new GitImportProjectsWizard(repo
, path
);
971 WizardDialog dlg
= new WizardDialog(getSite().getShell(), wiz
);
972 if (dlg
.open() == Window
.OK
)
973 // TODO if we drop the "existing projects" node, we can
974 // probably do without refresh
982 private void addActionsToToolbar() {
983 importAction
= new Action(UIText
.RepositoriesView_Import_Button
) {
987 GitCloneWizard wiz
= new GitCloneWizard();
988 wiz
.init(null, null);
989 new WizardDialog(getSite().getShell(), wiz
).open();
992 importAction
.setToolTipText(UIText
.RepositoriesView_Clone_Tooltip
);
994 importAction
.setImageDescriptor(UIIcons
.IMPORT
);
996 getViewSite().getActionBars().getToolBarManager().add(importAction
);
998 addAction
= new Action(UIText
.RepositoriesView_Add_Button
) {
1002 RepositorySearchDialog sd
= new RepositorySearchDialog(
1003 getSite().getShell(), getDirs());
1004 if (sd
.open() == Window
.OK
) {
1005 Set
<String
> dirs
= new HashSet
<String
>();
1006 dirs
.addAll(getDirs());
1007 if (dirs
.addAll(sd
.getDirectories()))
1014 addAction
.setToolTipText(UIText
.RepositoriesView_AddRepository_Tooltip
);
1016 addAction
.setImageDescriptor(UIIcons
.NEW_REPOSITORY
);
1018 getViewSite().getActionBars().getToolBarManager().add(addAction
);
1020 linkWithSelectionAction
= new Action(
1021 UIText
.RepositoriesView_LinkWithSelection_action
,
1022 IAction
.AS_CHECK_BOX
) {
1026 IEclipsePreferences prefs
= getPrefs();
1027 prefs
.putBoolean(PREFS_SYNCED
, isChecked());
1030 } catch (BackingStoreException e
) {
1034 ISelectionService srv
= (ISelectionService
) getSite()
1035 .getService(ISelectionService
.class);
1036 reactOnSelection(srv
.getSelection());
1043 linkWithSelectionAction
1044 .setToolTipText(UIText
.RepositoriesView_LinkWithSelection_action
);
1046 linkWithSelectionAction
.setImageDescriptor(UIIcons
.ELCL16_SYNCED
);
1048 linkWithSelectionAction
.setChecked(getPrefs().getBoolean(PREFS_SYNCED
,
1051 getViewSite().getActionBars().getToolBarManager().add(
1052 linkWithSelectionAction
);
1054 refreshAction
= new Action(UIText
.RepositoriesView_Refresh_Button
) {
1062 refreshAction
.setImageDescriptor(UIIcons
.ELCL16_REFRESH
);
1064 getViewSite().getActionBars().getToolBarManager().add(refreshAction
);
1068 * @return the preferences
1070 protected static IEclipsePreferences
getPrefs() {
1071 return new InstanceScope().getNode(Activator
.getPluginId());
1075 public void dispose() {
1076 // make sure to cancel the refresh job
1077 if (this.scheduledJob
!= null) {
1078 this.scheduledJob
.cancel();
1079 this.scheduledJob
= null;
1085 * Schedules a refreh
1087 public void scheduleRefresh() {
1089 Job job
= new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1092 protected IStatus
run(IProgressMonitor monitor
) {
1094 final List
<Repository
> input
;
1096 input
= getRepositoriesFromDirs(monitor
);
1097 } catch (InterruptedException e
) {
1098 return new Status(IStatus
.ERROR
, Activator
.getPluginId(), e
1102 Display
.getDefault().syncExec(new Runnable() {
1105 // keep expansion state and selection so that we can
1108 Object
[] expanded
= tv
.getExpandedElements();
1109 IStructuredSelection sel
= (IStructuredSelection
) tv
1112 tv
.setExpandedElements(expanded
);
1114 Object selected
= sel
.getFirstElement();
1115 if (selected
!= null)
1116 tv
.reveal(selected
);
1118 IViewPart part
= PlatformUI
.getWorkbench()
1119 .getActiveWorkbenchWindow().getActivePage()
1120 .findView(IPageLayout
.ID_PROP_SHEET
);
1122 PropertySheet sheet
= (PropertySheet
) part
;
1123 PropertySheetPage page
= (PropertySheetPage
) sheet
1130 return new Status(IStatus
.OK
, Activator
.getPluginId(), ""); //$NON-NLS-1$
1135 job
.setSystem(true);
1137 IWorkbenchSiteProgressService service
= (IWorkbenchSiteProgressService
) getSite()
1138 .getService(IWorkbenchSiteProgressService
.class);
1140 service
.schedule(job
);
1146 private List
<Repository
> getRepositoriesFromDirs(IProgressMonitor monitor
)
1147 throws InterruptedException
{
1149 List
<String
> gitDirStrings
= getDirs();
1150 List
<Repository
> input
= new ArrayList
<Repository
>();
1151 for (String dirString
: gitDirStrings
) {
1152 if (monitor
.isCanceled()) {
1153 throw new InterruptedException(
1154 UIText
.RepositoriesView_ActionCanceled_Message
);
1157 File dir
= new File(dirString
);
1158 if (dir
.exists() && dir
.isDirectory()) {
1159 input
.add(new Repository(dir
));
1161 } catch (IOException e
) {
1162 IStatus error
= new Status(IStatus
.ERROR
, Activator
1163 .getPluginId(), e
.getMessage(), e
);
1164 Activator
.getDefault().getLog().log(error
);
1171 * Adds a directory to the list if it is not already there
1174 * @return see {@link Collection#add(Object)}
1176 public static boolean addDir(File file
) {
1180 dirString
= file
.getCanonicalPath();
1181 } catch (IOException e
) {
1182 dirString
= file
.getAbsolutePath();
1185 List
<String
> dirStrings
= getDirs();
1186 if (dirStrings
.contains(dirString
)) {
1189 Set
<String
> dirs
= new HashSet
<String
>();
1190 dirs
.addAll(dirStrings
);
1191 dirs
.add(dirString
);
1197 private static void saveDirs(Set
<String
> gitDirStrings
) {
1198 StringBuilder sb
= new StringBuilder();
1199 for (String gitDirString
: gitDirStrings
) {
1200 sb
.append(gitDirString
);
1201 sb
.append(File
.pathSeparatorChar
);
1204 IEclipsePreferences prefs
= getPrefs();
1205 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
1208 } catch (BackingStoreException e
) {
1209 IStatus error
= new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1211 Activator
.getDefault().getLog().log(error
);
1216 public void setFocus() {
1217 tv
.getTree().setFocus();
1220 @SuppressWarnings("boxing")
1221 private boolean confirmProjectDeletion(List
<IProject
> projectsToDelete
) {
1223 confirmed
= MessageDialog
1225 getSite().getShell(),
1226 UIText
.RepositoriesView_ConfirmProjectDeletion_WindowTitle
,
1229 UIText
.RepositoriesView_ConfirmProjectDeletion_Question
,
1230 projectsToDelete
.size()));
1234 public void addSelectionChangedListener(ISelectionChangedListener listener
) {
1235 selectionListeners
.add(listener
);
1238 public ISelection
getSelection() {
1239 return currentSelection
;
1242 public void removeSelectionChangedListener(
1243 ISelectionChangedListener listener
) {
1244 selectionListeners
.remove(listener
);
1248 public void setSelection(ISelection selection
) {
1249 currentSelection
= selection
;
1250 for (ISelectionChangedListener listener
: selectionListeners
) {
1251 listener
.selectionChanged(new SelectionChangedEvent(
1252 RepositoriesView
.this, selection
));
1257 * Opens the tree and marks the folder to which a project is pointing
1262 @SuppressWarnings("unchecked")
1263 public void showResource(final IResource resource
) {
1264 IProject project
= resource
.getProject();
1265 RepositoryMapping mapping
= RepositoryMapping
.getMapping(project
);
1266 if (mapping
== null)
1269 if (addDir(mapping
.getRepository().getDirectory())) {
1273 boolean doSetSelection
= false;
1275 if (this.scheduledJob
!= null) {
1276 int state
= this.scheduledJob
.getState();
1277 if (state
== Job
.WAITING
|| state
== Job
.RUNNING
) {
1278 this.scheduledJob
.addJobChangeListener(new JobChangeAdapter() {
1281 public void done(IJobChangeEvent event
) {
1282 showResource(resource
);
1286 doSetSelection
= true;
1290 if (doSetSelection
) {
1291 RepositoriesViewContentProvider cp
= (RepositoriesViewContentProvider
) tv
1292 .getContentProvider();
1293 RepositoryTreeNode currentNode
= null;
1294 Object
[] repos
= cp
.getElements(tv
.getInput());
1295 for (Object repo
: repos
) {
1296 RepositoryTreeNode node
= (RepositoryTreeNode
) repo
;
1297 // TODO equals implementation of Repository?
1298 if (mapping
.getRepository().getDirectory().equals(
1299 ((Repository
) node
.getObject()).getDirectory())) {
1300 for (Object child
: cp
.getChildren(node
)) {
1301 RepositoryTreeNode childNode
= (RepositoryTreeNode
) child
;
1302 if (childNode
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1303 currentNode
= childNode
;
1311 IPath relPath
= new Path(mapping
.getRepoRelativePath(resource
));
1313 for (String segment
: relPath
.segments()) {
1314 for (Object child
: cp
.getChildren(currentNode
)) {
1315 RepositoryTreeNode
<File
> childNode
= (RepositoryTreeNode
<File
>) child
;
1316 if (childNode
.getObject().getName().equals(segment
)) {
1317 currentNode
= childNode
;
1323 final RepositoryTreeNode selNode
= currentNode
;
1325 Display
.getDefault().asyncExec(new Runnable() {
1328 tv
.setSelection(new StructuredSelection(selNode
), true);