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
.IJobChangeEvent
;
42 import org
.eclipse
.core
.runtime
.jobs
.Job
;
43 import org
.eclipse
.core
.runtime
.jobs
.JobChangeAdapter
;
44 import org
.eclipse
.core
.runtime
.preferences
.IEclipsePreferences
;
45 import org
.eclipse
.core
.runtime
.preferences
.InstanceScope
;
46 import org
.eclipse
.egit
.core
.op
.BranchOperation
;
47 import org
.eclipse
.egit
.core
.op
.ConnectProviderOperation
;
48 import org
.eclipse
.egit
.core
.project
.RepositoryMapping
;
49 import org
.eclipse
.egit
.ui
.Activator
;
50 import org
.eclipse
.egit
.ui
.UIIcons
;
51 import org
.eclipse
.egit
.ui
.UIText
;
52 import org
.eclipse
.egit
.ui
.internal
.clone
.GitCloneWizard
;
53 import org
.eclipse
.egit
.ui
.internal
.clone
.GitImportProjectsWizard
;
54 import org
.eclipse
.egit
.ui
.internal
.repository
.RepositoryTreeNode
.RepositoryTreeNodeType
;
55 import org
.eclipse
.jface
.action
.Action
;
56 import org
.eclipse
.jface
.action
.IAction
;
57 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
58 import org
.eclipse
.jface
.viewers
.ISelection
;
59 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
60 import org
.eclipse
.jface
.viewers
.ISelectionProvider
;
61 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
62 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
63 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
64 import org
.eclipse
.jface
.viewers
.TreeViewer
;
65 import org
.eclipse
.jface
.window
.Window
;
66 import org
.eclipse
.jface
.wizard
.Wizard
;
67 import org
.eclipse
.jface
.wizard
.WizardDialog
;
68 import org
.eclipse
.jgit
.lib
.Ref
;
69 import org
.eclipse
.jgit
.lib
.Repository
;
70 import org
.eclipse
.jgit
.lib
.RepositoryConfig
;
71 import org
.eclipse
.osgi
.util
.NLS
;
72 import org
.eclipse
.swt
.SWT
;
73 import org
.eclipse
.swt
.events
.MenuDetectEvent
;
74 import org
.eclipse
.swt
.events
.MenuDetectListener
;
75 import org
.eclipse
.swt
.events
.SelectionAdapter
;
76 import org
.eclipse
.swt
.events
.SelectionEvent
;
77 import org
.eclipse
.swt
.graphics
.Point
;
78 import org
.eclipse
.swt
.widgets
.Composite
;
79 import org
.eclipse
.swt
.widgets
.Display
;
80 import org
.eclipse
.swt
.widgets
.Menu
;
81 import org
.eclipse
.swt
.widgets
.MenuItem
;
82 import org
.eclipse
.swt
.widgets
.TreeItem
;
83 import org
.eclipse
.ui
.IEditorInput
;
84 import org
.eclipse
.ui
.IEditorPart
;
85 import org
.eclipse
.ui
.IFileEditorInput
;
86 import org
.eclipse
.ui
.IPageLayout
;
87 import org
.eclipse
.ui
.ISelectionListener
;
88 import org
.eclipse
.ui
.ISelectionService
;
89 import org
.eclipse
.ui
.IViewPart
;
90 import org
.eclipse
.ui
.IWorkbenchPart
;
91 import org
.eclipse
.ui
.PartInitException
;
92 import org
.eclipse
.ui
.PlatformUI
;
93 import org
.eclipse
.ui
.editors
.text
.EditorsUI
;
94 import org
.eclipse
.ui
.ide
.FileStoreEditorInput
;
95 import org
.eclipse
.ui
.ide
.IDE
;
96 import org
.eclipse
.ui
.part
.ViewPart
;
97 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
98 import org
.eclipse
.ui
.views
.properties
.IPropertySheetPage
;
99 import org
.eclipse
.ui
.views
.properties
.PropertySheet
;
100 import org
.eclipse
.ui
.views
.properties
.PropertySheetPage
;
101 import org
.osgi
.service
.prefs
.BackingStoreException
;
105 * The Git Repositories view.
107 * This keeps track of a bunch of local directory names each of which represent
108 * a Git Repository. This list is stored in some Preferences object and used to
109 * build the tree in the view.
111 * Implements {@link ISelectionProvider} in order to integrate with the
115 * <li>Clarification whether to show projects, perhaps configurable switch</li>
118 public class RepositoriesView
extends ViewPart
implements ISelectionProvider
{
121 public static final String VIEW_ID
= "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
123 // TODO central constants? RemoteConfig ones are private
124 static final String REMOTE
= "remote"; //$NON-NLS-1$
126 static final String URL
= "url"; //$NON-NLS-1$
128 static final String PUSHURL
= "pushurl"; //$NON-NLS-1$
130 static final String FETCH
= "fetch"; //$NON-NLS-1$
132 static final String PUSH
= "push"; //$NON-NLS-1$
134 private static final String PREFS_DIRECTORIES
= "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
136 private static final String PREFS_SYNCED
= "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
138 private final List
<ISelectionChangedListener
> selectionListeners
= new ArrayList
<ISelectionChangedListener
>();
140 private ISelection currentSelection
= new StructuredSelection();
142 private Job scheduledJob
;
144 private TreeViewer tv
;
146 private IAction importAction
;
148 private IAction addAction
;
150 private IAction refreshAction
;
152 private IAction linkWithSelectionAction
;
154 private static List
<String
> getDirs() {
155 List
<String
> resultStrings
= new ArrayList
<String
>();
156 String dirs
= getPrefs().get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
157 if (dirs
!= null && dirs
.length() > 0) {
158 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
159 while (tok
.hasMoreTokens()) {
160 String dirName
= tok
.nextToken();
161 File testFile
= new File(dirName
);
162 if (testFile
.exists()) {
163 resultStrings
.add(dirName
);
167 Collections
.sort(resultStrings
);
168 return resultStrings
;
171 private static void removeDir(File file
) {
175 dir
= file
.getCanonicalPath();
176 } catch (IOException e1
) {
177 dir
= file
.getAbsolutePath();
180 IEclipsePreferences prefs
= getPrefs();
182 TreeSet
<String
> resultStrings
= new TreeSet
<String
>();
183 String dirs
= prefs
.get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
184 if (dirs
!= null && dirs
.length() > 0) {
185 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
186 while (tok
.hasMoreTokens()) {
187 String dirName
= tok
.nextToken();
188 File testFile
= new File(dirName
);
189 if (testFile
.exists()) {
191 resultStrings
.add(testFile
.getCanonicalPath());
192 } catch (IOException e
) {
193 resultStrings
.add(testFile
.getAbsolutePath());
199 if (resultStrings
.remove(dir
)) {
200 StringBuilder sb
= new StringBuilder();
201 for (String gitDirString
: resultStrings
) {
202 sb
.append(gitDirString
);
203 sb
.append(File
.pathSeparatorChar
);
206 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
209 } catch (BackingStoreException e
) {
210 IStatus error
= new Status(IStatus
.ERROR
, Activator
211 .getPluginId(), e
.getMessage(), e
);
212 Activator
.getDefault().getLog().log(error
);
219 public Object
getAdapter(Class adapter
) {
220 // integrate with Properties view
221 if (adapter
== IPropertySheetPage
.class) {
222 PropertySheetPage page
= new PropertySheetPage();
224 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
229 return super.getAdapter(adapter
);
233 public void createPartControl(Composite parent
) {
234 tv
= new TreeViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
235 tv
.setContentProvider(new RepositoriesViewContentProvider());
236 // the label provider registers itself
237 new RepositoriesViewLabelProvider(tv
);
239 getSite().setSelectionProvider(this);
241 tv
.addSelectionChangedListener(new ISelectionChangedListener() {
243 public void selectionChanged(SelectionChangedEvent event
) {
245 IStructuredSelection ssel
= (IStructuredSelection
) event
247 if (ssel
.size() == 1) {
248 setSelection(new StructuredSelection(ssel
.getFirstElement()));
250 setSelection(new StructuredSelection());
258 addActionsToToolbar();
262 ISelectionService srv
= (ISelectionService
) getSite().getService(
263 ISelectionService
.class);
264 srv
.addPostSelectionListener(new ISelectionListener() {
266 public void selectionChanged(IWorkbenchPart part
,
267 ISelection selection
) {
269 // if the "link with selection" toggle is off, we're done
270 if (linkWithSelectionAction
== null
271 || !linkWithSelectionAction
.isChecked())
274 // this may happen if we switch between editors
275 if (part
instanceof IEditorPart
) {
276 IEditorInput input
= ((IEditorPart
) part
).getEditorInput();
277 if (input
instanceof IFileEditorInput
)
278 reactOnSelection(new StructuredSelection(
279 ((IFileEditorInput
) input
).getFile()));
282 reactOnSelection(selection
);
289 private void reactOnSelection(ISelection selection
) {
290 if (selection
instanceof StructuredSelection
) {
291 StructuredSelection ssel
= (StructuredSelection
) selection
;
292 if (ssel
.size() != 1)
294 if (ssel
.getFirstElement() instanceof IResource
) {
295 showResource((IResource
) ssel
.getFirstElement());
297 if (ssel
.getFirstElement() instanceof IAdaptable
) {
298 IResource adapted
= (IResource
) ((IAdaptable
) ssel
299 .getFirstElement()).getAdapter(IResource
.class);
301 showResource(adapted
);
306 private void addContextMenu() {
307 tv
.getTree().addMenuDetectListener(new MenuDetectListener() {
309 public void menuDetected(MenuDetectEvent e
) {
311 tv
.getTree().setMenu(null);
312 Menu men
= new Menu(tv
.getTree());
314 TreeItem testItem
= tv
.getTree().getItem(
315 tv
.getTree().toControl(new Point(e
.x
, e
.y
)));
316 if (testItem
== null) {
317 addMenuItemsForPanel(men
);
319 addMenuItemsForTreeSelection(men
);
322 tv
.getTree().setMenu(men
);
327 private void addMenuItemsForPanel(Menu men
) {
329 MenuItem importItem
= new MenuItem(men
, SWT
.PUSH
);
330 importItem
.setText(UIText
.RepositoriesView_ImportRepository_MenuItem
);
331 importItem
.addSelectionListener(new SelectionAdapter() {
334 public void widgetSelected(SelectionEvent e
) {
340 MenuItem addItem
= new MenuItem(men
, SWT
.PUSH
);
341 addItem
.setText(UIText
.RepositoriesView_AddRepository_MenuItem
);
342 addItem
.addSelectionListener(new SelectionAdapter() {
345 public void widgetSelected(SelectionEvent e
) {
351 MenuItem refreshItem
= new MenuItem(men
, SWT
.PUSH
);
352 refreshItem
.setText(refreshAction
.getText());
353 refreshItem
.addSelectionListener(new SelectionAdapter() {
356 public void widgetSelected(SelectionEvent e
) {
364 @SuppressWarnings("unchecked")
365 private void addMenuItemsForTreeSelection(Menu men
) {
367 final IStructuredSelection sel
= (IStructuredSelection
) tv
370 boolean importableProjectsOnly
= true;
372 for (Object node
: sel
.toArray()) {
373 RepositoryTreeNode tnode
= (RepositoryTreeNode
) node
;
374 importableProjectsOnly
= tnode
.getType() == RepositoryTreeNodeType
.PROJ
;
375 if (!importableProjectsOnly
)
379 if (importableProjectsOnly
) {
380 MenuItem sync
= new MenuItem(men
, SWT
.PUSH
);
381 sync
.setText(UIText
.RepositoriesView_ImportProject_MenuItem
);
383 sync
.addSelectionListener(new SelectionAdapter() {
386 public void widgetSelected(SelectionEvent e
) {
388 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
390 public void run(IProgressMonitor monitor
)
391 throws CoreException
{
393 for (Object selected
: sel
.toArray()) {
394 RepositoryTreeNode
<File
> projectNode
= (RepositoryTreeNode
<File
>) selected
;
395 File file
= projectNode
.getObject();
397 IProjectDescription pd
= ResourcesPlugin
398 .getWorkspace().newProjectDescription(
400 IPath locationPath
= new Path(file
403 pd
.setLocation(locationPath
);
405 ResourcesPlugin
.getWorkspace().getRoot()
406 .getProject(pd
.getName()).create(pd
,
408 IProject project
= ResourcesPlugin
409 .getWorkspace().getRoot().getProject(
411 project
.open(monitor
);
413 File gitDir
= projectNode
.getRepository()
416 ConnectProviderOperation connectProviderOperation
= new ConnectProviderOperation(
418 connectProviderOperation
419 .run(new SubProgressMonitor(monitor
, 20));
428 ResourcesPlugin
.getWorkspace().run(wsr
,
429 ResourcesPlugin
.getWorkspace().getRoot(),
430 IWorkspace
.AVOID_UPDATE
,
431 new NullProgressMonitor());
434 } catch (CoreException e1
) {
435 Activator
.getDefault().getLog().log(e1
.getStatus());
443 // from here on, we only deal with single selection
447 final RepositoryTreeNode node
= (RepositoryTreeNode
) sel
450 // for Refs (branches): checkout
451 if (node
.getType() == RepositoryTreeNodeType
.REF
) {
453 final Ref ref
= (Ref
) node
.getObject();
455 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
456 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
457 checkout
.addSelectionListener(new SelectionAdapter() {
460 public void widgetSelected(SelectionEvent e
) {
461 Repository repo
= node
.getRepository();
462 String refName
= ref
.getLeaf().getName();
463 final BranchOperation op
= new BranchOperation(repo
,
465 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
467 public void run(IProgressMonitor monitor
)
468 throws CoreException
{
473 ResourcesPlugin
.getWorkspace().run(wsr
,
474 ResourcesPlugin
.getWorkspace().getRoot(),
475 IWorkspace
.AVOID_UPDATE
,
476 new NullProgressMonitor());
478 } catch (CoreException e1
) {
479 MessageDialog
.openError(getSite().getShell(),
480 UIText
.RepositoriesView_Error_WindowTitle
, e1
489 // for Repository: import existing projects, remove, (delete), open
491 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
493 final Repository repo
= (Repository
) node
.getObject();
495 // TODO "import existing plug-in" menu item
497 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
498 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
499 remove
.addSelectionListener(new SelectionAdapter() {
502 public void widgetSelected(SelectionEvent e
) {
504 List
<IProject
> projectsToDelete
= new ArrayList
<IProject
>();
505 File workDir
= repo
.getWorkDir();
506 final IPath wdPath
= new Path(workDir
.getAbsolutePath());
507 for (IProject prj
: ResourcesPlugin
.getWorkspace()
508 .getRoot().getProjects()) {
509 if (wdPath
.isPrefixOf(prj
.getLocation())) {
510 projectsToDelete
.add(prj
);
514 if (!projectsToDelete
.isEmpty()) {
516 confirmed
= confirmProjectDeletion(projectsToDelete
);
522 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
524 public void run(IProgressMonitor monitor
)
525 throws CoreException
{
527 for (IProject prj
: ResourcesPlugin
.getWorkspace()
528 .getRoot().getProjects()) {
529 if (wdPath
.isPrefixOf(prj
.getLocation())) {
530 prj
.delete(false, false, monitor
);
534 removeDir(repo
.getDirectory());
540 ResourcesPlugin
.getWorkspace().run(wsr
,
541 ResourcesPlugin
.getWorkspace().getRoot(),
542 IWorkspace
.AVOID_UPDATE
,
543 new NullProgressMonitor());
544 } catch (CoreException e1
) {
545 Activator
.getDefault().getLog().log(e1
.getStatus());
552 // TODO delete does not work because of file locks on .pack-files
553 // Shawn Pearce has added the following thoughts:
555 // Hmm. We probably can't active detect file locks on pack files on
557 // It would be nice if we could support a delete, but only if the
559 // reasonably believed to be not-in-use right now.
561 // Within EGit you might be able to check GitProjectData and its
562 // repositoryCache to
563 // see if the repository is open by this workspace. If it is, then
564 // we know we shouldn't
567 // Some coding might look like this:
569 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
570 // deleteRepo.setText("Delete");
571 // deleteRepo.addSelectionListener(new SelectionAdapter() {
574 // public void widgetSelected(SelectionEvent e) {
576 // boolean confirmed = MessageDialog.openConfirm(getSite()
577 // .getShell(), "Confirm",
578 // "This will delete the repository, continue?");
583 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
585 // public void run(IProgressMonitor monitor)
586 // throws CoreException {
587 // File workDir = repos.get(0).getRepository()
590 // File gitDir = repos.get(0).getRepository()
593 // IPath wdPath = new Path(workDir.getAbsolutePath());
594 // for (IProject prj : ResourcesPlugin.getWorkspace()
595 // .getRoot().getProjects()) {
596 // if (wdPath.isPrefixOf(prj.getLocation())) {
597 // prj.delete(false, false, monitor);
601 // repos.get(0).getRepository().close();
603 // boolean deleted = deleteRecursively(gitDir, monitor);
605 // MessageDialog.openError(getSite().getShell(),
607 // "Could not delete Git Repository");
610 // deleted = deleteRecursively(workDir, monitor);
613 // .openError(getSite().getShell(),
615 // "Could not delete Git Working Directory");
618 // scheduleRefresh();
621 // private boolean deleteRecursively(File fileToDelete,
622 // IProgressMonitor monitor) {
623 // if (fileToDelete.isDirectory()) {
624 // for (File file : fileToDelete.listFiles()) {
625 // if (!deleteRecursively(file, monitor)) {
630 // monitor.setTaskName(fileToDelete.getAbsolutePath());
631 // boolean deleted = fileToDelete.delete();
633 // System.err.println("Could not delete "
634 // + fileToDelete.getAbsolutePath());
641 // ResourcesPlugin.getWorkspace().run(wsr,
642 // ResourcesPlugin.getWorkspace().getRoot(),
643 // IWorkspace.AVOID_UPDATE,
644 // new NullProgressMonitor());
645 // } catch (CoreException e1) {
646 // // TODO Exception handling
647 // e1.printStackTrace();
654 new MenuItem(men
, SWT
.SEPARATOR
);
656 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
657 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
658 openPropsView
.addSelectionListener(new SelectionAdapter() {
661 public void widgetSelected(SelectionEvent e
) {
663 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
664 .getActivePage().showView(
665 IPageLayout
.ID_PROP_SHEET
);
666 } catch (PartInitException e1
) {
674 if (node
.getType() == RepositoryTreeNodeType
.REMOTES
) {
676 MenuItem remoteConfig
= new MenuItem(men
, SWT
.PUSH
);
677 remoteConfig
.setText(UIText
.RepositoriesView_NewRemoteMenu
);
678 remoteConfig
.addSelectionListener(new SelectionAdapter() {
681 public void widgetSelected(SelectionEvent e
) {
682 WizardDialog dialog
= new WizardDialog(
683 getSite().getShell(), new ConfigureRemoteWizard(
684 node
.getRepository()));
685 if (dialog
.open() == Window
.OK
) {
693 if (node
.getType() == RepositoryTreeNodeType
.REMOTE
) {
695 final String name
= (String
) node
.getObject();
697 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
699 .setText(UIText
.RepositoriesView_ConfigureFetchMenu
);
700 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
703 public void widgetSelected(SelectionEvent e
) {
704 WizardDialog dialog
= new WizardDialog(
705 getSite().getShell(), new ConfigureRemoteWizard(
706 node
.getRepository(), name
, false));
707 if (dialog
.open() == Window
.OK
) {
714 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
715 configureUrlPush
.setText(UIText
.RepositoriesView_ConfigurePushMenu
);
716 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
719 public void widgetSelected(SelectionEvent e
) {
720 WizardDialog dialog
= new WizardDialog(
721 getSite().getShell(), new ConfigureRemoteWizard(
722 node
.getRepository(), name
, true));
723 if (dialog
.open() == Window
.OK
) {
730 new MenuItem(men
, SWT
.SEPARATOR
);
732 MenuItem removeRemote
= new MenuItem(men
, SWT
.PUSH
);
733 removeRemote
.setText(UIText
.RepositoriesView_RemoveRemoteMenu
);
734 removeRemote
.addSelectionListener(new SelectionAdapter() {
737 public void widgetSelected(SelectionEvent e
) {
739 boolean ok
= MessageDialog
741 getSite().getShell(),
742 UIText
.RepositoriesView_ConfirmDeleteRemoteHeader
,
745 UIText
.RepositoriesView_ConfirmDeleteRemoteMessage
,
748 RepositoryConfig config
= node
.getRepository()
750 config
.unsetSection(REMOTE
, name
);
754 } catch (IOException e1
) {
755 MessageDialog
.openError(getSite().getShell(),
756 UIText
.RepositoriesView_ErrorHeader
, e1
765 new MenuItem(men
, SWT
.SEPARATOR
);
767 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
768 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
769 openPropsView
.addSelectionListener(new SelectionAdapter() {
772 public void widgetSelected(SelectionEvent e
) {
774 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
775 .getActivePage().showView(
776 IPageLayout
.ID_PROP_SHEET
);
777 } catch (PartInitException e1
) {
785 if (node
.getType() == RepositoryTreeNodeType
.FILE
) {
787 final File file
= (File
) node
.getObject();
789 MenuItem openInTextEditor
= new MenuItem(men
, SWT
.PUSH
);
791 .setText(UIText
.RepositoriesView_OpenInTextEditor_menu
);
792 openInTextEditor
.addSelectionListener(new SelectionAdapter() {
795 public void widgetSelected(SelectionEvent e
) {
796 IFileStore store
= EFS
.getLocalFileSystem().getStore(
797 new Path(file
.getAbsolutePath()));
799 // TODO do we need a read-only editor here?
800 IDE
.openEditor(getSite().getPage(),
801 new FileStoreEditorInput(store
),
802 EditorsUI
.DEFAULT_TEXT_EDITOR_ID
);
804 } catch (PartInitException e1
) {
805 MessageDialog
.openError(getSite().getShell(),
806 UIText
.RepositoriesView_Error_WindowTitle
, e1
815 if (node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
816 String path
= ((File
) node
.getObject()).getAbsolutePath();
817 createImportProjectItem(men
, node
.getRepository(), path
);
820 if (node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
821 String path
= node
.getRepository().getWorkDir().getAbsolutePath();
822 createImportProjectItem(men
, node
.getRepository(), path
);
827 private void createImportProjectItem(Menu men
, final Repository repo
,
829 MenuItem importProjects
;
830 importProjects
= new MenuItem(men
, SWT
.PUSH
);
832 .setText(UIText
.RepositoriesView_ImportExistingProjects_MenuItem
);
833 importProjects
.addSelectionListener(new SelectionAdapter() {
836 public void widgetSelected(SelectionEvent e
) {
837 // Instead of the generic ExternalProjectImportWizard
838 // from the org.eclipse.ui.ide plug-in, we use our
839 // own wizard (the generic one does not allow to set the
840 // path in 3.4; in addition, we have added project filtering
842 Wizard wiz
= new GitImportProjectsWizard(repo
, path
);
844 WizardDialog dlg
= new WizardDialog(getSite().getShell(), wiz
);
845 if (dlg
.open() == Window
.OK
)
846 // TODO if we drop the "existing projects" node, we can
847 // probably do without refresh
855 private void addActionsToToolbar() {
856 importAction
= new Action(UIText
.RepositoriesView_Import_Button
) {
860 GitCloneWizard wiz
= new GitCloneWizard();
861 wiz
.init(null, null);
862 new WizardDialog(getSite().getShell(), wiz
).open();
865 importAction
.setToolTipText(UIText
.RepositoriesView_Clone_Tooltip
);
867 importAction
.setImageDescriptor(UIIcons
.IMPORT
);
869 getViewSite().getActionBars().getToolBarManager().add(importAction
);
871 addAction
= new Action(UIText
.RepositoriesView_Add_Button
) {
875 RepositorySearchDialog sd
= new RepositorySearchDialog(
876 getSite().getShell(), getDirs());
877 if (sd
.open() == Window
.OK
) {
878 Set
<String
> dirs
= new HashSet
<String
>();
879 dirs
.addAll(getDirs());
880 if (dirs
.addAll(sd
.getDirectories()))
887 addAction
.setToolTipText(UIText
.RepositoriesView_AddRepository_Tooltip
);
889 addAction
.setImageDescriptor(UIIcons
.NEW_REPOSITORY
);
891 getViewSite().getActionBars().getToolBarManager().add(addAction
);
893 linkWithSelectionAction
= new Action(
894 UIText
.RepositoriesView_LinkWithSelection_action
,
895 IAction
.AS_CHECK_BOX
) {
899 IEclipsePreferences prefs
= getPrefs();
900 prefs
.putBoolean(PREFS_SYNCED
, isChecked());
903 } catch (BackingStoreException e
) {
907 ISelectionService srv
= (ISelectionService
) getSite()
908 .getService(ISelectionService
.class);
909 reactOnSelection(srv
.getSelection());
916 linkWithSelectionAction
917 .setToolTipText(UIText
.RepositoriesView_LinkWithSelection_action
);
919 linkWithSelectionAction
.setImageDescriptor(UIIcons
.ELCL16_SYNCED
);
921 linkWithSelectionAction
.setChecked(getPrefs().getBoolean(PREFS_SYNCED
,
924 getViewSite().getActionBars().getToolBarManager().add(
925 linkWithSelectionAction
);
927 refreshAction
= new Action(UIText
.RepositoriesView_Refresh_Button
) {
935 refreshAction
.setImageDescriptor(UIIcons
.ELCL16_REFRESH
);
937 getViewSite().getActionBars().getToolBarManager().add(refreshAction
);
941 * @return the preferences
943 protected static IEclipsePreferences
getPrefs() {
944 return new InstanceScope().getNode(Activator
.getPluginId());
948 public void dispose() {
949 // make sure to cancel the refresh job
950 if (this.scheduledJob
!= null) {
951 this.scheduledJob
.cancel();
952 this.scheduledJob
= null;
960 public void scheduleRefresh() {
962 Job job
= new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
965 protected IStatus
run(IProgressMonitor monitor
) {
967 final List
<Repository
> input
;
969 input
= getRepositoriesFromDirs(monitor
);
970 } catch (InterruptedException e
) {
971 return new Status(IStatus
.ERROR
, Activator
.getPluginId(), e
975 Display
.getDefault().syncExec(new Runnable() {
978 // keep expansion state and selection so that we can
981 Object
[] expanded
= tv
.getExpandedElements();
982 IStructuredSelection sel
= (IStructuredSelection
) tv
985 tv
.setExpandedElements(expanded
);
987 Object selected
= sel
.getFirstElement();
988 if (selected
!= null)
991 IViewPart part
= PlatformUI
.getWorkbench()
992 .getActiveWorkbenchWindow().getActivePage()
993 .findView(IPageLayout
.ID_PROP_SHEET
);
995 PropertySheet sheet
= (PropertySheet
) part
;
996 PropertySheetPage page
= (PropertySheetPage
) sheet
1003 return new Status(IStatus
.OK
, Activator
.getPluginId(), ""); //$NON-NLS-1$
1008 job
.setSystem(true);
1010 IWorkbenchSiteProgressService service
= (IWorkbenchSiteProgressService
) getSite()
1011 .getService(IWorkbenchSiteProgressService
.class);
1013 service
.schedule(job
);
1019 private List
<Repository
> getRepositoriesFromDirs(IProgressMonitor monitor
)
1020 throws InterruptedException
{
1022 List
<String
> gitDirStrings
= getDirs();
1023 List
<Repository
> input
= new ArrayList
<Repository
>();
1024 for (String dirString
: gitDirStrings
) {
1025 if (monitor
.isCanceled()) {
1026 throw new InterruptedException(
1027 UIText
.RepositoriesView_ActionCanceled_Message
);
1030 File dir
= new File(dirString
);
1031 if (dir
.exists() && dir
.isDirectory()) {
1032 input
.add(new Repository(dir
));
1034 } catch (IOException e
) {
1035 IStatus error
= new Status(IStatus
.ERROR
, Activator
1036 .getPluginId(), e
.getMessage(), e
);
1037 Activator
.getDefault().getLog().log(error
);
1044 * Adds a directory to the list if it is not already there
1047 * @return see {@link Collection#add(Object)}
1049 public static boolean addDir(File file
) {
1053 dirString
= file
.getCanonicalPath();
1054 } catch (IOException e
) {
1055 dirString
= file
.getAbsolutePath();
1058 List
<String
> dirStrings
= getDirs();
1059 if (dirStrings
.contains(dirString
)) {
1062 Set
<String
> dirs
= new HashSet
<String
>();
1063 dirs
.addAll(dirStrings
);
1064 dirs
.add(dirString
);
1070 private static void saveDirs(Set
<String
> gitDirStrings
) {
1071 StringBuilder sb
= new StringBuilder();
1072 for (String gitDirString
: gitDirStrings
) {
1073 sb
.append(gitDirString
);
1074 sb
.append(File
.pathSeparatorChar
);
1077 IEclipsePreferences prefs
= getPrefs();
1078 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
1081 } catch (BackingStoreException e
) {
1082 IStatus error
= new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1084 Activator
.getDefault().getLog().log(error
);
1089 public void setFocus() {
1090 tv
.getTree().setFocus();
1093 @SuppressWarnings("boxing")
1094 private boolean confirmProjectDeletion(List
<IProject
> projectsToDelete
) {
1096 confirmed
= MessageDialog
1098 getSite().getShell(),
1099 UIText
.RepositoriesView_ConfirmProjectDeletion_WindowTitle
,
1102 UIText
.RepositoriesView_ConfirmProjectDeletion_Question
,
1103 projectsToDelete
.size()));
1107 public void addSelectionChangedListener(ISelectionChangedListener listener
) {
1108 selectionListeners
.add(listener
);
1111 public ISelection
getSelection() {
1112 return currentSelection
;
1115 public void removeSelectionChangedListener(
1116 ISelectionChangedListener listener
) {
1117 selectionListeners
.remove(listener
);
1121 public void setSelection(ISelection selection
) {
1122 currentSelection
= selection
;
1123 for (ISelectionChangedListener listener
: selectionListeners
) {
1124 listener
.selectionChanged(new SelectionChangedEvent(
1125 RepositoriesView
.this, selection
));
1130 * Opens the tree and marks the folder to which a project is pointing
1135 @SuppressWarnings("unchecked")
1136 public void showResource(final IResource resource
) {
1137 IProject project
= resource
.getProject();
1138 RepositoryMapping mapping
= RepositoryMapping
.getMapping(project
);
1139 if (mapping
== null)
1142 if (addDir(mapping
.getRepository().getDirectory())) {
1146 boolean doSetSelection
= false;
1148 if (this.scheduledJob
!= null) {
1149 int state
= this.scheduledJob
.getState();
1150 if (state
== Job
.WAITING
|| state
== Job
.RUNNING
) {
1151 this.scheduledJob
.addJobChangeListener(new JobChangeAdapter() {
1154 public void done(IJobChangeEvent event
) {
1155 showResource(resource
);
1159 doSetSelection
= true;
1163 if (doSetSelection
) {
1164 RepositoriesViewContentProvider cp
= (RepositoriesViewContentProvider
) tv
1165 .getContentProvider();
1166 RepositoryTreeNode currentNode
= null;
1167 Object
[] repos
= cp
.getElements(tv
.getInput());
1168 for (Object repo
: repos
) {
1169 RepositoryTreeNode node
= (RepositoryTreeNode
) repo
;
1170 // TODO equals implementation of Repository?
1171 if (mapping
.getRepository().getDirectory().equals(
1172 ((Repository
) node
.getObject()).getDirectory())) {
1173 for (Object child
: cp
.getChildren(node
)) {
1174 RepositoryTreeNode childNode
= (RepositoryTreeNode
) child
;
1175 if (childNode
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1176 currentNode
= childNode
;
1184 IPath relPath
= new Path(mapping
.getRepoRelativePath(resource
));
1186 for (String segment
: relPath
.segments()) {
1187 for (Object child
: cp
.getChildren(currentNode
)) {
1188 RepositoryTreeNode
<File
> childNode
= (RepositoryTreeNode
<File
>) child
;
1189 if (childNode
.getObject().getName().equals(segment
)) {
1190 currentNode
= childNode
;
1196 final RepositoryTreeNode selNode
= currentNode
;
1198 Display
.getDefault().asyncExec(new Runnable() {
1201 tv
.setSelection(new StructuredSelection(selNode
), true);