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
.util
.ArrayList
;
16 import java
.util
.Collection
;
17 import java
.util
.Collections
;
18 import java
.util
.HashSet
;
19 import java
.util
.List
;
21 import java
.util
.StringTokenizer
;
22 import java
.util
.TreeSet
;
24 import org
.eclipse
.core
.filesystem
.EFS
;
25 import org
.eclipse
.core
.filesystem
.IFileStore
;
26 import org
.eclipse
.core
.resources
.IProject
;
27 import org
.eclipse
.core
.resources
.IProjectDescription
;
28 import org
.eclipse
.core
.resources
.IResource
;
29 import org
.eclipse
.core
.resources
.IWorkspace
;
30 import org
.eclipse
.core
.resources
.IWorkspaceRunnable
;
31 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
32 import org
.eclipse
.core
.runtime
.CoreException
;
33 import org
.eclipse
.core
.runtime
.IAdaptable
;
34 import org
.eclipse
.core
.runtime
.IPath
;
35 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
36 import org
.eclipse
.core
.runtime
.IStatus
;
37 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
38 import org
.eclipse
.core
.runtime
.Path
;
39 import org
.eclipse
.core
.runtime
.Status
;
40 import org
.eclipse
.core
.runtime
.SubProgressMonitor
;
41 import org
.eclipse
.core
.runtime
.jobs
.Job
;
42 import org
.eclipse
.core
.runtime
.preferences
.IEclipsePreferences
;
43 import org
.eclipse
.core
.runtime
.preferences
.InstanceScope
;
44 import org
.eclipse
.egit
.core
.op
.BranchOperation
;
45 import org
.eclipse
.egit
.core
.op
.ConnectProviderOperation
;
46 import org
.eclipse
.egit
.core
.project
.RepositoryMapping
;
47 import org
.eclipse
.egit
.ui
.Activator
;
48 import org
.eclipse
.egit
.ui
.UIIcons
;
49 import org
.eclipse
.egit
.ui
.UIText
;
50 import org
.eclipse
.egit
.ui
.internal
.clone
.GitCloneWizard
;
51 import org
.eclipse
.egit
.ui
.internal
.repository
.RepositoryTreeNode
.RepositoryTreeNodeType
;
52 import org
.eclipse
.jface
.action
.Action
;
53 import org
.eclipse
.jface
.action
.IAction
;
54 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
55 import org
.eclipse
.jface
.viewers
.ISelection
;
56 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
57 import org
.eclipse
.jface
.viewers
.ISelectionProvider
;
58 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
59 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
60 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
61 import org
.eclipse
.jface
.viewers
.TreeViewer
;
62 import org
.eclipse
.jface
.window
.Window
;
63 import org
.eclipse
.jface
.wizard
.Wizard
;
64 import org
.eclipse
.jface
.wizard
.WizardDialog
;
65 import org
.eclipse
.jgit
.lib
.Ref
;
66 import org
.eclipse
.jgit
.lib
.Repository
;
67 import org
.eclipse
.jgit
.lib
.RepositoryConfig
;
68 import org
.eclipse
.osgi
.util
.NLS
;
69 import org
.eclipse
.swt
.SWT
;
70 import org
.eclipse
.swt
.events
.MenuDetectEvent
;
71 import org
.eclipse
.swt
.events
.MenuDetectListener
;
72 import org
.eclipse
.swt
.events
.SelectionAdapter
;
73 import org
.eclipse
.swt
.events
.SelectionEvent
;
74 import org
.eclipse
.swt
.graphics
.Point
;
75 import org
.eclipse
.swt
.layout
.FillLayout
;
76 import org
.eclipse
.swt
.widgets
.Composite
;
77 import org
.eclipse
.swt
.widgets
.Display
;
78 import org
.eclipse
.swt
.widgets
.Menu
;
79 import org
.eclipse
.swt
.widgets
.MenuItem
;
80 import org
.eclipse
.swt
.widgets
.TreeItem
;
81 import org
.eclipse
.ui
.IPageLayout
;
82 import org
.eclipse
.ui
.ISelectionListener
;
83 import org
.eclipse
.ui
.ISelectionService
;
84 import org
.eclipse
.ui
.IViewPart
;
85 import org
.eclipse
.ui
.IWorkbenchPart
;
86 import org
.eclipse
.ui
.PartInitException
;
87 import org
.eclipse
.ui
.PlatformUI
;
88 import org
.eclipse
.ui
.editors
.text
.EditorsUI
;
89 import org
.eclipse
.ui
.ide
.FileStoreEditorInput
;
90 import org
.eclipse
.ui
.ide
.IDE
;
91 import org
.eclipse
.ui
.part
.ViewPart
;
92 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
93 import org
.eclipse
.ui
.views
.properties
.IPropertySheetPage
;
94 import org
.eclipse
.ui
.views
.properties
.PropertySheet
;
95 import org
.eclipse
.ui
.views
.properties
.PropertySheetPage
;
96 import org
.eclipse
.ui
.wizards
.datatransfer
.ExternalProjectImportWizard
;
97 import org
.osgi
.service
.prefs
.BackingStoreException
;
101 * The Git Repositories view.
103 * This keeps track of a bunch of local directory names each of which represent
104 * a Git Repository. This list is stored in some Preferences object and used to
105 * build the tree in the view.
107 * Implements {@link ISelectionProvider} in order to integrate with the
111 * <li>Clarification whether to show projects, perhaps configurable switch</li>
114 public class RepositoriesView
extends ViewPart
implements ISelectionProvider
{
117 public static final String VIEW_ID
= "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
118 // TODO central constants? RemoteConfig ones are private
119 static final String REMOTE
= "remote"; //$NON-NLS-1$
121 static final String URL
= "url"; //$NON-NLS-1$
123 static final String PUSHURL
= "pushurl"; //$NON-NLS-1$
125 static final String FETCH
= "fetch"; //$NON-NLS-1$
127 static final String PUSH
= "push"; //$NON-NLS-1$
129 private static final String PREFS_DIRECTORIES
= "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
131 private static final String PREFS_SYNCED
= "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
133 private final List
<ISelectionChangedListener
> selectionListeners
= new ArrayList
<ISelectionChangedListener
>();
135 private ISelection currentSelection
= new StructuredSelection();
137 private Job scheduledJob
;
139 private TreeViewer tv
;
141 private IAction importAction
;
143 private IAction addAction
;
145 private IAction refreshAction
;
147 private IAction linkWithSelectionAction
;
149 private static List
<String
> getDirs() {
150 List
<String
> resultStrings
= new ArrayList
<String
>();
151 String dirs
= getPrefs().get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
152 if (dirs
!= null && dirs
.length() > 0) {
153 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
154 while (tok
.hasMoreTokens()) {
155 String dirName
= tok
.nextToken();
156 File testFile
= new File(dirName
);
157 if (testFile
.exists()) {
158 resultStrings
.add(dirName
);
162 Collections
.sort(resultStrings
);
163 return resultStrings
;
166 private static void removeDir(File file
) {
170 dir
= file
.getCanonicalPath();
171 } catch (IOException e1
) {
172 dir
= file
.getAbsolutePath();
175 IEclipsePreferences prefs
= getPrefs();
177 TreeSet
<String
> resultStrings
= new TreeSet
<String
>();
178 String dirs
= prefs
.get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
179 if (dirs
!= null && dirs
.length() > 0) {
180 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
181 while (tok
.hasMoreTokens()) {
182 String dirName
= tok
.nextToken();
183 File testFile
= new File(dirName
);
184 if (testFile
.exists()) {
186 resultStrings
.add(testFile
.getCanonicalPath());
187 } catch (IOException e
) {
188 resultStrings
.add(testFile
.getAbsolutePath());
194 if (resultStrings
.remove(dir
)) {
195 StringBuilder sb
= new StringBuilder();
196 for (String gitDirString
: resultStrings
) {
197 sb
.append(gitDirString
);
198 sb
.append(File
.pathSeparatorChar
);
201 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
204 } catch (BackingStoreException e
) {
205 IStatus error
= new Status(IStatus
.ERROR
, Activator
206 .getPluginId(), e
.getMessage(), e
);
207 Activator
.getDefault().getLog().log(error
);
214 public Object
getAdapter(Class adapter
) {
215 // integrate with Properties view
216 if (adapter
== IPropertySheetPage
.class) {
217 PropertySheetPage page
= new PropertySheetPage();
219 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
224 return super.getAdapter(adapter
);
228 public void createPartControl(Composite parent
) {
230 Composite main
= new Composite(parent
, SWT
.NONE
);
231 main
.setLayout(new FillLayout());
233 tv
= new TreeViewer(main
);
234 tv
.setContentProvider(new RepositoriesViewContentProvider());
235 // the label provider registers itself
236 new RepositoriesViewLabelProvider(tv
);
238 getSite().setSelectionProvider(this);
240 tv
.addSelectionChangedListener(new ISelectionChangedListener() {
242 public void selectionChanged(SelectionChangedEvent event
) {
244 IStructuredSelection ssel
= (IStructuredSelection
) event
246 if (ssel
.size() == 1) {
247 setSelection(new StructuredSelection(ssel
.getFirstElement()));
249 setSelection(new StructuredSelection());
254 // make the tree rather wide to accommodate long directory names
255 tv
.getTree().getColumn(0).setWidth(700);
259 addActionsToToolbar();
263 ISelectionService srv
= (ISelectionService
) getSite().getService(
264 ISelectionService
.class);
265 srv
.addPostSelectionListener(new ISelectionListener() {
267 public void selectionChanged(IWorkbenchPart part
,
268 ISelection selection
) {
270 if (linkWithSelectionAction
== null
271 || !linkWithSelectionAction
.isChecked())
274 reactOnSelection(selection
);
280 private void reactOnSelection(ISelection selection
) {
281 if (selection
instanceof StructuredSelection
) {
282 StructuredSelection ssel
= (StructuredSelection
) selection
;
283 if (ssel
.size() != 1)
285 if (ssel
.getFirstElement() instanceof IResource
) {
286 showResource((IResource
) ssel
.getFirstElement());
288 if (ssel
.getFirstElement() instanceof IAdaptable
) {
289 IResource adapted
= (IResource
) ((IAdaptable
) ssel
290 .getFirstElement()).getAdapter(IResource
.class);
292 showResource(adapted
);
297 private void addContextMenu() {
298 tv
.getTree().addMenuDetectListener(new MenuDetectListener() {
300 public void menuDetected(MenuDetectEvent e
) {
302 tv
.getTree().setMenu(null);
303 Menu men
= new Menu(tv
.getTree());
305 TreeItem testItem
= tv
.getTree().getItem(
306 tv
.getTree().toControl(new Point(e
.x
, e
.y
)));
307 if (testItem
== null) {
308 addMenuItemsForPanel(men
);
310 addMenuItemsForTreeSelection(men
);
313 tv
.getTree().setMenu(men
);
318 private void addMenuItemsForPanel(Menu men
) {
320 MenuItem importItem
= new MenuItem(men
, SWT
.PUSH
);
321 importItem
.setText(UIText
.RepositoriesView_ImportRepository_MenuItem
);
322 importItem
.addSelectionListener(new SelectionAdapter() {
325 public void widgetSelected(SelectionEvent e
) {
331 MenuItem addItem
= new MenuItem(men
, SWT
.PUSH
);
332 addItem
.setText(UIText
.RepositoriesView_AddRepository_MenuItem
);
333 addItem
.addSelectionListener(new SelectionAdapter() {
336 public void widgetSelected(SelectionEvent e
) {
342 MenuItem refreshItem
= new MenuItem(men
, SWT
.PUSH
);
343 refreshItem
.setText(refreshAction
.getText());
344 refreshItem
.addSelectionListener(new SelectionAdapter() {
347 public void widgetSelected(SelectionEvent e
) {
355 @SuppressWarnings("unchecked")
356 private void addMenuItemsForTreeSelection(Menu men
) {
358 final IStructuredSelection sel
= (IStructuredSelection
) tv
361 boolean importableProjectsOnly
= true;
363 for (Object node
: sel
.toArray()) {
364 RepositoryTreeNode tnode
= (RepositoryTreeNode
) node
;
365 importableProjectsOnly
= tnode
.getType() == RepositoryTreeNodeType
.PROJ
;
366 if (!importableProjectsOnly
)
370 if (importableProjectsOnly
) {
371 MenuItem sync
= new MenuItem(men
, SWT
.PUSH
);
372 sync
.setText(UIText
.RepositoriesView_ImportProject_MenuItem
);
374 sync
.addSelectionListener(new SelectionAdapter() {
377 public void widgetSelected(SelectionEvent e
) {
379 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
381 public void run(IProgressMonitor monitor
)
382 throws CoreException
{
384 for (Object selected
: sel
.toArray()) {
385 RepositoryTreeNode
<File
> projectNode
= (RepositoryTreeNode
<File
>) selected
;
386 File file
= projectNode
.getObject();
388 IProjectDescription pd
= ResourcesPlugin
389 .getWorkspace().newProjectDescription(
391 IPath locationPath
= new Path(file
394 pd
.setLocation(locationPath
);
396 ResourcesPlugin
.getWorkspace().getRoot()
397 .getProject(pd
.getName()).create(pd
,
399 IProject project
= ResourcesPlugin
400 .getWorkspace().getRoot().getProject(
402 project
.open(monitor
);
404 File gitDir
= projectNode
.getRepository()
407 ConnectProviderOperation connectProviderOperation
= new ConnectProviderOperation(
409 connectProviderOperation
410 .run(new SubProgressMonitor(monitor
, 20));
419 ResourcesPlugin
.getWorkspace().run(wsr
,
420 ResourcesPlugin
.getWorkspace().getRoot(),
421 IWorkspace
.AVOID_UPDATE
,
422 new NullProgressMonitor());
425 } catch (CoreException e1
) {
426 Activator
.getDefault().getLog().log(e1
.getStatus());
434 // from here on, we only deal with single selection
438 final RepositoryTreeNode node
= (RepositoryTreeNode
) sel
441 // for Refs (branches): checkout
442 if (node
.getType() == RepositoryTreeNodeType
.REF
) {
444 final Ref ref
= (Ref
) node
.getObject();
446 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
447 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
448 checkout
.addSelectionListener(new SelectionAdapter() {
451 public void widgetSelected(SelectionEvent e
) {
452 Repository repo
= node
.getRepository();
453 String refName
= ref
.getLeaf().getName();
454 final BranchOperation op
= new BranchOperation(repo
,
456 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
458 public void run(IProgressMonitor monitor
)
459 throws CoreException
{
464 ResourcesPlugin
.getWorkspace().run(wsr
,
465 ResourcesPlugin
.getWorkspace().getRoot(),
466 IWorkspace
.AVOID_UPDATE
,
467 new NullProgressMonitor());
469 } catch (CoreException e1
) {
470 MessageDialog
.openError(getSite().getShell(),
471 UIText
.RepositoriesView_Error_WindowTitle
, e1
480 // for Repository: import existing projects, remove, (delete), open
482 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
484 final Repository repo
= (Repository
) node
.getObject();
486 // TODO "import existing plug-in" menu item
488 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
489 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
490 remove
.addSelectionListener(new SelectionAdapter() {
493 public void widgetSelected(SelectionEvent e
) {
495 List
<IProject
> projectsToDelete
= new ArrayList
<IProject
>();
496 File workDir
= repo
.getWorkDir();
497 final IPath wdPath
= new Path(workDir
.getAbsolutePath());
498 for (IProject prj
: ResourcesPlugin
.getWorkspace()
499 .getRoot().getProjects()) {
500 if (wdPath
.isPrefixOf(prj
.getLocation())) {
501 projectsToDelete
.add(prj
);
505 if (!projectsToDelete
.isEmpty()) {
507 confirmed
= confirmProjectDeletion(projectsToDelete
);
513 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
515 public void run(IProgressMonitor monitor
)
516 throws CoreException
{
518 for (IProject prj
: ResourcesPlugin
.getWorkspace()
519 .getRoot().getProjects()) {
520 if (wdPath
.isPrefixOf(prj
.getLocation())) {
521 prj
.delete(false, false, monitor
);
525 removeDir(repo
.getDirectory());
531 ResourcesPlugin
.getWorkspace().run(wsr
,
532 ResourcesPlugin
.getWorkspace().getRoot(),
533 IWorkspace
.AVOID_UPDATE
,
534 new NullProgressMonitor());
535 } catch (CoreException e1
) {
536 Activator
.getDefault().getLog().log(e1
.getStatus());
543 // TODO delete does not work because of file locks on .pack-files
544 // Shawn Pearce has added the following thoughts:
546 // Hmm. We probably can't active detect file locks on pack files on
548 // It would be nice if we could support a delete, but only if the
550 // reasonably believed to be not-in-use right now.
552 // Within EGit you might be able to check GitProjectData and its
553 // repositoryCache to
554 // see if the repository is open by this workspace. If it is, then
555 // we know we shouldn't
558 // Some coding might look like this:
560 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
561 // deleteRepo.setText("Delete");
562 // deleteRepo.addSelectionListener(new SelectionAdapter() {
565 // public void widgetSelected(SelectionEvent e) {
567 // boolean confirmed = MessageDialog.openConfirm(getSite()
568 // .getShell(), "Confirm",
569 // "This will delete the repository, continue?");
574 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
576 // public void run(IProgressMonitor monitor)
577 // throws CoreException {
578 // File workDir = repos.get(0).getRepository()
581 // File gitDir = repos.get(0).getRepository()
584 // IPath wdPath = new Path(workDir.getAbsolutePath());
585 // for (IProject prj : ResourcesPlugin.getWorkspace()
586 // .getRoot().getProjects()) {
587 // if (wdPath.isPrefixOf(prj.getLocation())) {
588 // prj.delete(false, false, monitor);
592 // repos.get(0).getRepository().close();
594 // boolean deleted = deleteRecursively(gitDir, monitor);
596 // MessageDialog.openError(getSite().getShell(),
598 // "Could not delete Git Repository");
601 // deleted = deleteRecursively(workDir, monitor);
604 // .openError(getSite().getShell(),
606 // "Could not delete Git Working Directory");
609 // scheduleRefresh();
612 // private boolean deleteRecursively(File fileToDelete,
613 // IProgressMonitor monitor) {
614 // if (fileToDelete.isDirectory()) {
615 // for (File file : fileToDelete.listFiles()) {
616 // if (!deleteRecursively(file, monitor)) {
621 // monitor.setTaskName(fileToDelete.getAbsolutePath());
622 // boolean deleted = fileToDelete.delete();
624 // System.err.println("Could not delete "
625 // + fileToDelete.getAbsolutePath());
632 // ResourcesPlugin.getWorkspace().run(wsr,
633 // ResourcesPlugin.getWorkspace().getRoot(),
634 // IWorkspace.AVOID_UPDATE,
635 // new NullProgressMonitor());
636 // } catch (CoreException e1) {
637 // // TODO Exception handling
638 // e1.printStackTrace();
645 new MenuItem(men
, SWT
.SEPARATOR
);
647 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
648 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
649 openPropsView
.addSelectionListener(new SelectionAdapter() {
652 public void widgetSelected(SelectionEvent e
) {
654 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
655 .getActivePage().showView(
656 IPageLayout
.ID_PROP_SHEET
);
657 } catch (PartInitException e1
) {
665 if (node
.getType() == RepositoryTreeNodeType
.REMOTES
) {
667 MenuItem remoteConfig
= new MenuItem(men
, SWT
.PUSH
);
668 remoteConfig
.setText(UIText
.RepositoriesView_NewRemoteMenu
);
669 remoteConfig
.addSelectionListener(new SelectionAdapter() {
672 public void widgetSelected(SelectionEvent e
) {
673 new WizardDialog(getSite().getShell(),
674 new ConfigureRemoteWizard(node
.getRepository()))
683 if (node
.getType() == RepositoryTreeNodeType
.REMOTE
) {
685 final String name
= (String
) node
.getObject();
687 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
689 .setText(UIText
.RepositoriesView_ConfigureFetchMenu
);
690 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
693 public void widgetSelected(SelectionEvent e
) {
695 new WizardDialog(getSite().getShell(),
696 new ConfigureRemoteWizard(node
.getRepository(),
697 name
, false)).open();
704 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
705 configureUrlPush
.setText(UIText
.RepositoriesView_ConfigurePushMenu
);
706 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
709 public void widgetSelected(SelectionEvent e
) {
711 new WizardDialog(getSite().getShell(),
712 new ConfigureRemoteWizard(node
.getRepository(),
720 new MenuItem(men
, SWT
.SEPARATOR
);
722 MenuItem removeRemote
= new MenuItem(men
, SWT
.PUSH
);
723 removeRemote
.setText(UIText
.RepositoriesView_RemoveRemoteMenu
);
724 removeRemote
.addSelectionListener(new SelectionAdapter() {
727 public void widgetSelected(SelectionEvent e
) {
729 boolean ok
= MessageDialog
731 getSite().getShell(),
732 UIText
.RepositoriesView_ConfirmDeleteRemoteHeader
,
735 UIText
.RepositoriesView_ConfirmDeleteRemoteMessage
,
738 RepositoryConfig config
= node
.getRepository()
740 config
.unsetSection(REMOTE
, name
);
744 } catch (IOException e1
) {
745 MessageDialog
.openError(getSite().getShell(),
746 UIText
.RepositoriesView_ErrorHeader
, e1
755 new MenuItem(men
, SWT
.SEPARATOR
);
757 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
758 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
759 openPropsView
.addSelectionListener(new SelectionAdapter() {
762 public void widgetSelected(SelectionEvent e
) {
764 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
765 .getActivePage().showView(
766 IPageLayout
.ID_PROP_SHEET
);
767 } catch (PartInitException e1
) {
775 if (node
.getType() == RepositoryTreeNodeType
.FILE
) {
777 final File file
= (File
) node
.getObject();
779 MenuItem openInTextEditor
= new MenuItem(men
, SWT
.PUSH
);
781 .setText(UIText
.RepositoriesView_OpenInTextEditor_menu
);
782 openInTextEditor
.addSelectionListener(new SelectionAdapter() {
785 public void widgetSelected(SelectionEvent e
) {
786 IFileStore store
= EFS
.getLocalFileSystem().getStore(
787 new Path(file
.getAbsolutePath()));
789 // TODO do we need a read-only editor here?
790 IDE
.openEditor(getSite().getPage(),
791 new FileStoreEditorInput(store
),
792 EditorsUI
.DEFAULT_TEXT_EDITOR_ID
);
794 } catch (PartInitException e1
) {
795 MessageDialog
.openError(getSite().getShell(),
796 UIText
.RepositoriesView_Error_WindowTitle
, e1
805 if (node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
806 String path
= ((File
) node
.getObject()).getAbsolutePath();
807 createImportProjectItem(men
, node
.getRepository(), path
);
810 if (node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
811 String path
= node
.getRepository().getWorkDir().getAbsolutePath();
812 createImportProjectItem(men
, node
.getRepository(), path
);
817 private void createImportProjectItem(Menu men
, final Repository repo
,
819 MenuItem importProjects
;
820 importProjects
= new MenuItem(men
, SWT
.PUSH
);
822 .setText(UIText
.RepositoriesView_ImportExistingProjects_MenuItem
);
823 importProjects
.addSelectionListener(new SelectionAdapter() {
826 public void widgetSelected(SelectionEvent e
) {
827 Wizard wiz
= new ExternalProjectImportWizard(path
) {
830 public void addPages() {
832 // we could add some page with a single
836 public boolean performFinish() {
838 final Set
<IPath
> previousLocations
= new HashSet
<IPath
>();
839 // we want to share only new projects
840 for (IProject project
: ResourcesPlugin
.getWorkspace()
841 .getRoot().getProjects()) {
842 previousLocations
.add(project
.getLocation());
845 boolean success
= super.performFinish();
847 // IWizardPage page = getPage("Share");
848 // TODO evaluate checkbox or such, but
850 // always, we don't even need another
853 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
855 public void run(IProgressMonitor monitor
)
856 throws CoreException
{
857 File gitDir
= repo
.getDirectory();
858 File gitWorkDir
= repo
.getWorkDir();
859 Path workPath
= new Path(gitWorkDir
862 // we check which projects are
864 // pointing to a location in the
868 for (IProject prj
: ResourcesPlugin
869 .getWorkspace().getRoot()
872 if (workPath
.isPrefixOf(prj
874 if (previousLocations
.contains(prj
878 ConnectProviderOperation connectProviderOperation
= new ConnectProviderOperation(
880 connectProviderOperation
881 .run(new SubProgressMonitor(
891 ResourcesPlugin
.getWorkspace().run(
893 ResourcesPlugin
.getWorkspace()
895 IWorkspace
.AVOID_UPDATE
,
896 new NullProgressMonitor());
898 } catch (CoreException ce
) {
902 UIText
.RepositoriesView_Error_WindowTitle
,
912 WizardDialog dlg
= new WizardDialog(getSite().getShell(), wiz
);
919 private void addActionsToToolbar() {
920 importAction
= new Action(UIText
.RepositoriesView_Import_Button
) {
924 GitCloneWizard wiz
= new GitCloneWizard();
925 wiz
.init(null, null);
926 new WizardDialog(getSite().getShell(), wiz
).open();
929 importAction
.setToolTipText(UIText
.RepositoriesView_Clone_Tooltip
);
931 importAction
.setImageDescriptor(UIIcons
.IMPORT
);
933 getViewSite().getActionBars().getToolBarManager().add(importAction
);
935 addAction
= new Action(UIText
.RepositoriesView_Add_Button
) {
939 RepositorySearchDialog sd
= new RepositorySearchDialog(
940 getSite().getShell(), getDirs());
941 if (sd
.open() == Window
.OK
) {
942 Set
<String
> dirs
= new HashSet
<String
>();
943 dirs
.addAll(getDirs());
944 if (dirs
.addAll(sd
.getDirectories()))
951 addAction
.setToolTipText(UIText
.RepositoriesView_AddRepository_Tooltip
);
953 addAction
.setImageDescriptor(UIIcons
.NEW_REPOSITORY
);
955 getViewSite().getActionBars().getToolBarManager().add(addAction
);
957 linkWithSelectionAction
= new Action(UIText
.RepositoriesView_LinkWithSelection_action
,
958 IAction
.AS_CHECK_BOX
) {
962 IEclipsePreferences prefs
= getPrefs();
963 prefs
.putBoolean(PREFS_SYNCED
, isChecked());
966 } catch (BackingStoreException e
) {
970 ISelectionService srv
= (ISelectionService
) getSite()
971 .getService(ISelectionService
.class);
972 reactOnSelection(srv
.getSelection());
979 linkWithSelectionAction
.setToolTipText(UIText
.RepositoriesView_LinkWithSelection_action
);
981 linkWithSelectionAction
.setImageDescriptor(UIIcons
.ELCL16_SYNCED
);
983 linkWithSelectionAction
.setChecked(getPrefs().getBoolean(PREFS_SYNCED
,
986 getViewSite().getActionBars().getToolBarManager().add(
987 linkWithSelectionAction
);
989 refreshAction
= new Action(UIText
.RepositoriesView_Refresh_Button
) {
997 refreshAction
.setImageDescriptor(UIIcons
.ELCL16_REFRESH
);
999 getViewSite().getActionBars().getToolBarManager().add(refreshAction
);
1003 * @return the preferences
1005 protected static IEclipsePreferences
getPrefs() {
1006 return new InstanceScope().getNode(Activator
.getPluginId());
1010 public void dispose() {
1011 // make sure to cancel the refresh job
1012 if (this.scheduledJob
!= null) {
1013 this.scheduledJob
.cancel();
1014 this.scheduledJob
= null;
1020 * Schedules a refreh
1022 public void scheduleRefresh() {
1024 Job job
= new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1027 protected IStatus
run(IProgressMonitor monitor
) {
1029 final List
<Repository
> input
;
1031 input
= getRepositoriesFromDirs(monitor
);
1032 } catch (InterruptedException e
) {
1033 return new Status(IStatus
.ERROR
, Activator
.getPluginId(), e
1037 Display
.getDefault().syncExec(new Runnable() {
1040 // keep expansion state and selection so that we can
1043 Object
[] expanded
= tv
.getExpandedElements();
1044 IStructuredSelection sel
= (IStructuredSelection
) tv
1047 tv
.setExpandedElements(expanded
);
1049 Object selected
= sel
.getFirstElement();
1050 if (selected
!= null)
1051 tv
.reveal(selected
);
1053 IViewPart part
= PlatformUI
.getWorkbench()
1054 .getActiveWorkbenchWindow().getActivePage()
1055 .findView(IPageLayout
.ID_PROP_SHEET
);
1057 PropertySheet sheet
= (PropertySheet
) part
;
1058 PropertySheetPage page
= (PropertySheetPage
) sheet
1065 return new Status(IStatus
.OK
, Activator
.getPluginId(), ""); //$NON-NLS-1$
1070 job
.setSystem(true);
1072 IWorkbenchSiteProgressService service
= (IWorkbenchSiteProgressService
) getSite()
1073 .getService(IWorkbenchSiteProgressService
.class);
1075 service
.schedule(job
);
1081 private List
<Repository
> getRepositoriesFromDirs(IProgressMonitor monitor
)
1082 throws InterruptedException
{
1084 List
<String
> gitDirStrings
= getDirs();
1085 List
<Repository
> input
= new ArrayList
<Repository
>();
1086 for (String dirString
: gitDirStrings
) {
1087 if (monitor
.isCanceled()) {
1088 throw new InterruptedException(
1089 UIText
.RepositoriesView_ActionCanceled_Message
);
1092 File dir
= new File(dirString
);
1093 if (dir
.exists() && dir
.isDirectory()) {
1094 input
.add(new Repository(dir
));
1096 } catch (IOException e
) {
1097 IStatus error
= new Status(IStatus
.ERROR
, Activator
1098 .getPluginId(), e
.getMessage(), e
);
1099 Activator
.getDefault().getLog().log(error
);
1106 * Adds a directory to the list if it is not already there
1109 * @return see {@link Collection#add(Object)}
1111 public static boolean addDir(File file
) {
1115 dirString
= file
.getCanonicalPath();
1116 } catch (IOException e
) {
1117 dirString
= file
.getAbsolutePath();
1120 List
<String
> dirStrings
= getDirs();
1121 if (dirStrings
.contains(dirString
)) {
1124 Set
<String
> dirs
= new HashSet
<String
>();
1125 dirs
.addAll(dirStrings
);
1126 dirs
.add(dirString
);
1132 private static void saveDirs(Set
<String
> gitDirStrings
) {
1133 StringBuilder sb
= new StringBuilder();
1134 for (String gitDirString
: gitDirStrings
) {
1135 sb
.append(gitDirString
);
1136 sb
.append(File
.pathSeparatorChar
);
1139 IEclipsePreferences prefs
= getPrefs();
1140 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
1143 } catch (BackingStoreException e
) {
1144 IStatus error
= new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1146 Activator
.getDefault().getLog().log(error
);
1151 public void setFocus() {
1155 @SuppressWarnings("boxing")
1156 private boolean confirmProjectDeletion(List
<IProject
> projectsToDelete
) {
1158 confirmed
= MessageDialog
1160 getSite().getShell(),
1161 UIText
.RepositoriesView_ConfirmProjectDeletion_WindowTitle
,
1164 UIText
.RepositoriesView_ConfirmProjectDeletion_Question
,
1165 projectsToDelete
.size()));
1169 public void addSelectionChangedListener(ISelectionChangedListener listener
) {
1170 selectionListeners
.add(listener
);
1173 public ISelection
getSelection() {
1174 return currentSelection
;
1177 public void removeSelectionChangedListener(
1178 ISelectionChangedListener listener
) {
1179 selectionListeners
.remove(listener
);
1183 public void setSelection(ISelection selection
) {
1184 currentSelection
= selection
;
1185 for (ISelectionChangedListener listener
: selectionListeners
) {
1186 listener
.selectionChanged(new SelectionChangedEvent(
1187 RepositoriesView
.this, selection
));
1192 * Opens the tree and marks the folder to which a project is pointing
1197 @SuppressWarnings("unchecked")
1198 public void showResource(IResource resource
) {
1199 IProject project
= resource
.getProject();
1200 RepositoryMapping mapping
= RepositoryMapping
.getMapping(project
);
1201 if (mapping
== null)
1204 if (addDir(mapping
.getRepository().getDirectory())) {
1207 scheduledJob
.join();
1208 } catch (InterruptedException e
) {
1213 RepositoriesViewContentProvider cp
= (RepositoriesViewContentProvider
) tv
1214 .getContentProvider();
1215 RepositoryTreeNode currentNode
= null;
1216 Object
[] repos
= cp
.getElements(tv
.getInput());
1217 for (Object repo
: repos
) {
1218 RepositoryTreeNode node
= (RepositoryTreeNode
) repo
;
1219 // TODO equals implementation of Repository?
1220 if (mapping
.getRepository().getDirectory().equals(
1221 ((Repository
) node
.getObject()).getDirectory())) {
1222 for (Object child
: cp
.getChildren(node
)) {
1223 RepositoryTreeNode childNode
= (RepositoryTreeNode
) child
;
1224 if (childNode
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1225 currentNode
= childNode
;
1233 IPath relPath
= new Path(mapping
.getRepoRelativePath(resource
));
1235 for (String segment
: relPath
.segments()) {
1236 for (Object child
: cp
.getChildren(currentNode
)) {
1237 RepositoryTreeNode
<File
> childNode
= (RepositoryTreeNode
<File
>) child
;
1238 if (childNode
.getObject().getName().equals(segment
)) {
1239 currentNode
= childNode
;
1245 tv
.setSelection(new StructuredSelection(currentNode
), true);