From f0325b28ed76b4115f3ed2cb2dde57fa2d9af38e Mon Sep 17 00:00:00 2001 From: Mathias Kinzler Date: Mon, 15 Mar 2010 13:14:29 +0100 Subject: [PATCH] Add a "Git Repositories View" This is a very first implementation of a "Git Repositories View" (analogous to e.g. CVS Repositories View). It allows addition and removal of local Git Repositories, cloning of Git Repositories as well as checking-out of branches. Project import can be done using the standard "Import Existing Projects" wizard. We should use bug 301168 to discuss this. Bug: 301168 (also: 281394, 302742, 303403) Change-Id: Icd0a7171f67c3180dc4a4a4246b490d6d4536845 Signed-off-by: Mathias Kinzler --- org.eclipse.egit.ui/icons/obj16/branches_rep.gif | Bin 0 -> 121 bytes org.eclipse.egit.ui/icons/obj16/repository_rep.gif | Bin 0 -> 545 bytes org.eclipse.egit.ui/icons/ovr/checkedout_ov.gif | Bin 0 -> 173 bytes org.eclipse.egit.ui/plugin.xml | 14 + .../src/org/eclipse/egit/ui/Activator.java | 17 + .../ui/internal/repository/RepositoriesView.java | 1506 ++++++++++++++++++++ .../repository/RepositorySearchDialog.java | 243 ++++ .../internal/repository/RepositoryViewUITexts.java | 72 + .../repository/repositoryviewuitexts.properties | 18 + 9 files changed, 1870 insertions(+) create mode 100644 org.eclipse.egit.ui/icons/obj16/branches_rep.gif create mode 100644 org.eclipse.egit.ui/icons/obj16/repository_rep.gif create mode 100644 org.eclipse.egit.ui/icons/ovr/checkedout_ov.gif create mode 100644 org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java create mode 100644 org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositorySearchDialog.java create mode 100644 org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoryViewUITexts.java create mode 100644 org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/repositoryviewuitexts.properties diff --git a/org.eclipse.egit.ui/icons/obj16/branches_rep.gif b/org.eclipse.egit.ui/icons/obj16/branches_rep.gif new file mode 100644 index 0000000000000000000000000000000000000000..717f3b5b6476e0b6fa12aa66b29126d744302997 GIT binary patch literal 121 zcwTe&bhEHb6krfwSj50!9<$WE>1fQ-qf3t-{r~?z0}LqsWMO1rU}4Y!aX@MrnC(1v z$sF9k_+inUE2|iee7PEyDtu5kW}DAp)7FUWviV(%x}0acde_@49NheRV~aE6hZTlG Ssk4MOdTOYyjBsUQum%86sVVya literal 0 HcwPel00001 diff --git a/org.eclipse.egit.ui/icons/obj16/repository_rep.gif b/org.eclipse.egit.ui/icons/obj16/repository_rep.gif new file mode 100644 index 0000000000000000000000000000000000000000..0470e1588d9373bf8069ec51e259b32ff8dd4140 GIT binary patch literal 545 zcwTe&bhEHb6krfwc*el+^F!j#52=5C%=zOq_^@kI-zfbD?KCS!VgyZ{TP6rm+pV<&{Y@zM(MfQ)D zi5!|~dUT%kv4!@ZHp(BGYII<#$&uMskLC#P?bSat)%^ZU(H)K2yV`7awAyTIvE9{X zyS>F`TeI!fM%zOThKCvrpH{N%uQl3NWBjy|eM7F=<}~&H|Nk=#5uo^!g^_{5ok0i0 z0mTUedtHNjQ*%pen}UIkp^3RdTa%QsJPW6&tiDvgu8Q20Y5K1HR>qQyg3{WK&i!_Z z{PU)-ZnIs-!XYxj>^n7PEJJ6uxZW#SgU ldC5$Ifk#H$#jRiC@zdun0{!j^dRm%Brs|)5H#ag^0{}5rvJ?OS literal 0 HcwPel00001 diff --git a/org.eclipse.egit.ui/icons/ovr/checkedout_ov.gif b/org.eclipse.egit.ui/icons/ovr/checkedout_ov.gif new file mode 100644 index 0000000000000000000000000000000000000000..0053b569833265caaca5028164834ef75c9a403a GIT binary patch literal 173 zcwTe&bhEHbjNIrl)P_Y-;VTtfnWcVk)m@E+U~MC9kb* + + + + + + diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java index d9b05fa4..cd8f92a5 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java @@ -51,6 +51,16 @@ import org.eclipse.jgit.transport.SshSessionFactory; */ public class Activator extends AbstractUIPlugin { + /** the repository icon */ + public static final String ICON_REPOSITORY = "Icon_Repository"; //$NON-NLS-1$ + + /** the branches icon */ + public static final String ICON_BRANCHES = "Icon_Branches"; //$NON-NLS-1$ + + /** the checked-out overlay icon */ + public static final String ICON_CHECKEDOUT_OVR = "Icon_CheckedOut_Overlay"; //$NON-NLS-1$ + + /** * The one and only instance */ @@ -196,6 +206,13 @@ public class Activator extends AbstractUIPlugin { setupProxy(context); setupRepoChangeScanner(); setupRepoIndexRefresh(); + setupImageRegistry(); + } + + private void setupImageRegistry() { + getImageRegistry().put(ICON_REPOSITORY, imageDescriptorFromPlugin(getPluginId(), "icons/obj16/repository_rep.gif")); //$NON-NLS-1$ + getImageRegistry().put(ICON_BRANCHES, imageDescriptorFromPlugin(getPluginId(), "icons/obj16/branches_rep.gif")); //$NON-NLS-1$ + getImageRegistry().put(ICON_CHECKEDOUT_OVR, imageDescriptorFromPlugin(getPluginId(), "icons/ovr/checkedout_ov.gif")); //$NON-NLS-1$ } private void setupRepoIndexRefresh() { diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java new file mode 100644 index 00000000..25a9b940 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoriesView.java @@ -0,0 +1,1506 @@ +/******************************************************************************* + * Copyright (c) 2010 SAP AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mathias Kinzler (SAP AG) - initial implementation + *******************************************************************************/ +package org.eclipse.egit.ui.internal.repository; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeSet; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.egit.core.op.BranchOperation; +import org.eclipse.egit.core.op.ConnectProviderOperation; +import org.eclipse.egit.ui.Activator; +import org.eclipse.egit.ui.internal.clone.GitCloneWizard; +import org.eclipse.egit.ui.internal.clone.GitProjectsImportPage; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.resource.CompositeImageDescriptor; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.DefaultInformationControl; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.DefaultInformationControl.IInformationPresenter; +import org.eclipse.jface.viewers.BaseLabelProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryCache; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MenuDetectEvent; +import org.eclipse.swt.events.MenuDetectListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackAdapter; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE.SharedImages; +import org.eclipse.ui.part.ViewPart; +import org.eclipse.ui.progress.IWorkbenchSiteProgressService; +import org.eclipse.ui.statushandlers.StatusManager; +import org.eclipse.ui.wizards.datatransfer.ExternalProjectImportWizard; +import org.osgi.service.prefs.BackingStoreException; + +/** + * + * The Git Repositories view. + *

+ * This keeps track of a bunch of local directory names each of which represent + * a Git Repository. This list is stored in some Preferences object and used to + * build the tree in the view. + *

+ * TODO + *

  • Icons
  • + *
  • String externalization
  • + *
  • Clarification whether to show projects, perhaps configurable switch
  • + * + */ +public class RepositoriesView extends ViewPart { + + private static final String PREFS_DIRECTORIES = "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$ + + private static final ImageDescriptor CHECKEDOUT_OVERLAY = Activator + .getDefault().getImageRegistry().getDescriptor( + Activator.ICON_CHECKEDOUT_OVR); + + private Job scheduledJob; + + private TreeViewer tv; + + private IAction importAction; + + private IAction addAction; + + private IAction refreshAction; + + enum RepositoryTreeNodeType { + + REPO(Activator.ICON_REPOSITORY), REF(PlatformUI.getWorkbench() + .getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER)), PROJ( + PlatformUI.getWorkbench().getSharedImages().getImage( + SharedImages.IMG_OBJ_PROJECT_CLOSED)), BRANCHES( + Activator.ICON_BRANCHES), PROJECTS(PlatformUI.getWorkbench() + .getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER)); + + private final Image myImage; + + private RepositoryTreeNodeType(String iconName) { + + if (iconName != null) { + myImage = Activator.getDefault().getImageRegistry().get( + iconName); + } else { + myImage = null; + } + + } + + private RepositoryTreeNodeType(Image icon) { + myImage = icon; + + } + + public Image getIcon() { + return myImage; + } + + } + + private static final class RepositoryTreeNode { + + private final Repository myRepository; + + private final T myObject; + + private final RepositoryTreeNodeType myType; + + private final RepositoryTreeNode myParent; + + private String branch; + + public RepositoryTreeNode(RepositoryTreeNode parent, + RepositoryTreeNodeType type, Repository repository, T treeObject) { + myParent = parent; + myRepository = repository; + myType = type; + myObject = treeObject; + } + + @SuppressWarnings("unchecked") + private RepositoryTreeNode getRepositoryNode() { + if (myType == RepositoryTreeNodeType.REPO) { + return (RepositoryTreeNode) this; + } else { + return getParent().getRepositoryNode(); + } + } + + /** + * We keep this cached in the repository node to avoid repeated lookups + * + * @return the full branch + * @throws IOException + */ + public String getBranch() throws IOException { + if (myType != RepositoryTreeNodeType.REPO) { + return getRepositoryNode().getBranch(); + } + if (branch == null) { + branch = getRepository().getBranch(); + } + return branch; + } + + public RepositoryTreeNode getParent() { + return myParent; + } + + public RepositoryTreeNodeType getType() { + return myType; + } + + public Repository getRepository() { + return myRepository; + } + + public T getObject() { + return myObject; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + switch (myType) { + case REPO: + case PROJECTS: + case BRANCHES: + result = prime + * result + + ((myObject == null) ? 0 : ((Repository) myObject) + .getDirectory().hashCode()); + break; + case REF: + result = prime + * result + + ((myObject == null) ? 0 : ((Ref) myObject).getName() + .hashCode()); + break; + case PROJ: + result = prime + * result + + ((myObject == null) ? 0 : ((File) myObject).getPath() + .hashCode()); + break; + + default: + break; + } + + result = prime * result + + ((myParent == null) ? 0 : myParent.hashCode()); + result = prime + * result + + ((myRepository == null) ? 0 : myRepository.getDirectory() + .hashCode()); + result = prime * result + + ((myType == null) ? 0 : myType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + + RepositoryTreeNode other = (RepositoryTreeNode) obj; + + if (myType == null) { + if (other.myType != null) + return false; + } else if (!myType.equals(other.myType)) + return false; + if (myParent == null) { + if (other.myParent != null) + return false; + } else if (!myParent.equals(other.myParent)) + return false; + if (myRepository == null) { + if (other.myRepository != null) + return false; + } else if (!myRepository.getDirectory().equals( + other.myRepository.getDirectory())) + return false; + if (myObject == null) { + if (other.myObject != null) + return false; + } else if (!checkObjectsEqual(other.myObject)) + return false; + + return true; + } + + private boolean checkObjectsEqual(Object otherObject) { + switch (myType) { + case REPO: + case PROJECTS: + case BRANCHES: + return ((Repository) myObject).getDirectory().equals( + ((Repository) otherObject).getDirectory()); + case REF: + return ((Ref) myObject).getName().equals( + ((Ref) otherObject).getName()); + case PROJ: + return ((File) myObject).getPath().equals( + ((File) otherObject).getPath()); + default: + return false; + } + } + } + + private static final class ContentProvider implements ITreeContentProvider { + + @SuppressWarnings("unchecked") + public Object[] getElements(Object inputElement) { + + Comparator> sorter = new Comparator>() { + + public int compare(RepositoryTreeNode o1, + RepositoryTreeNode o2) { + return getRepositoryName(o1.getObject()).compareTo( + getRepositoryName(o2.getObject())); + } + + }; + + Set> output = new TreeSet>( + sorter); + + for (Repository repo : ((List) inputElement)) { + output.add(new RepositoryTreeNode(null, + RepositoryTreeNodeType.REPO, repo, repo)); + } + + return output.toArray(); + } + + public void dispose() { + // nothing + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // nothing + } + + public Object[] getChildren(Object parentElement) { + + RepositoryTreeNode node = (RepositoryTreeNode) parentElement; + Repository repo = node.getRepository(); + + switch (node.getType()) { + + case BRANCHES: + + List> refs = new ArrayList>(); + + Repository rep = node.getRepository(); + for (Ref ref : rep.getAllRefs().values()) { + refs.add(new RepositoryTreeNode(node, + RepositoryTreeNodeType.REF, repo, ref)); + } + + return refs.toArray(); + + case REPO: + + List> branches = new ArrayList>(); + + branches.add(new RepositoryTreeNode(node, + RepositoryTreeNodeType.BRANCHES, node.getRepository(), + node.getRepository())); + + branches.add(new RepositoryTreeNode(node, + RepositoryTreeNodeType.PROJECTS, node.getRepository(), + node.getRepository())); + + return branches.toArray(); + + case PROJECTS: + + List> projects = new ArrayList>(); + + // TODO do we want to show the projects here? + Collection result = new HashSet(); + Set traversed = new HashSet(); + collectProjectFilesFromDirectory(result, repo.getDirectory() + .getParentFile(), traversed, new NullProgressMonitor()); + for (File file : result) { + projects.add(new RepositoryTreeNode(node, + RepositoryTreeNodeType.PROJ, repo, file)); + } + + Comparator> sorter = new Comparator>() { + + public int compare(RepositoryTreeNode o1, + RepositoryTreeNode o2) { + return o1.getObject().getName().compareTo( + o2.getObject().getName()); + } + }; + Collections.sort(projects, sorter); + + return projects.toArray(); + + default: + return null; + } + + } + + public Object getParent(Object element) { + + return ((RepositoryTreeNode) element).getParent(); + } + + public boolean hasChildren(Object element) { + Object[] children = getChildren(element); + return children != null && children.length > 0; + } + + private boolean collectProjectFilesFromDirectory( + Collection files, File directory, + Set directoriesVisited, IProgressMonitor monitor) { + + // stolen from the GitCloneWizard; perhaps we should completely drop + // the projects from this view, though + if (monitor.isCanceled()) { + return false; + } + monitor.subTask(NLS.bind( + RepositoryViewUITexts.RepositoriesView_Checking_Message, + directory.getPath())); + File[] contents = directory.listFiles(); + if (contents == null) + return false; + + // first look for project description files + final String dotProject = IProjectDescription.DESCRIPTION_FILE_NAME; + for (int i = 0; i < contents.length; i++) { + File file = contents[i]; + if (file.isFile() && file.getName().equals(dotProject)) { + files.add(file.getParentFile()); + // don't search sub-directories since we can't have nested + // projects + return true; + } + } + // no project description found, so recurse into sub-directories + for (int i = 0; i < contents.length; i++) { + if (contents[i].isDirectory()) { + if (!contents[i].getName().equals( + GitProjectsImportPage.METADATA_FOLDER)) { + try { + String canonicalPath = contents[i] + .getCanonicalPath(); + if (!directoriesVisited.add(canonicalPath)) { + // already been here --> do not recurse + continue; + } + } catch (IOException exception) { + StatusManager.getManager().handle( + new Status(IStatus.ERROR, Activator + .getPluginId(), exception + .getLocalizedMessage(), exception)); + + } + collectProjectFilesFromDirectory(files, contents[i], + directoriesVisited, monitor); + } + } + } + return true; + } + + } + + private static final class LabelProvider extends BaseLabelProvider + implements ITableLabelProvider { + + private DefaultInformationControl infoControl; + + /** + * + * @param viewer + */ + LabelProvider(final TreeViewer viewer) { + + viewer.setLabelProvider(this); + Tree tree = viewer.getTree(); + TreeColumn col = new TreeColumn(tree, SWT.NONE); + col.setWidth(400); + viewer.getTree().addMouseTrackListener(new MouseTrackAdapter() { + + @Override + public void mouseHover(MouseEvent e) { + + Point eventPoint = new Point(e.x, e.y); + + TreeItem item = viewer.getTree().getItem(eventPoint); + if (item != null) { + + RepositoryTreeNode node = (RepositoryTreeNode) item + .getData(); + String text = node.getRepository().getDirectory() + .getAbsolutePath(); + + final ViewerCell cell = viewer.getCell(eventPoint); + + if (infoControl != null && infoControl.isVisible()) { + infoControl.setVisible(false); + } + + GC testGc = new GC(cell.getControl()); + final Point textExtent = testGc.textExtent(text); + testGc.dispose(); + + if (infoControl == null || !infoControl.isVisible()) { + + IInformationPresenter ips = new IInformationPresenter() { + + public String updatePresentation( + Display display, String hoverInfo, + TextPresentation presentation, + int maxWidth, int maxHeight) { + return hoverInfo; + } + + }; + + infoControl = new DefaultInformationControl(Display + .getCurrent().getActiveShell().getShell(), + ips) { + + @Override + public void setInformation(String content) { + super.setInformation(content); + super.setSize(textExtent.x, textExtent.y); + } + + }; + } + + Point dispPoint = viewer.getControl().toDisplay( + eventPoint); + + infoControl.setLocation(dispPoint); + + // the default info provider works better with \r ... + infoControl.setInformation(text); + + final MouseMoveListener moveListener = new MouseMoveListener() { + + public void mouseMove(MouseEvent evt) { + infoControl.setVisible(false); + cell.getControl().removeMouseMoveListener(this); + + } + }; + + cell.getControl().addMouseMoveListener(moveListener); + + infoControl.setVisible(true); + + } + + } + + }); + + } + + public Image getColumnImage(Object element, int columnIndex) { + return decorateImage(((RepositoryTreeNode) element).getType() + .getIcon(), element); + } + + public String getColumnText(Object element, int columnIndex) { + + RepositoryTreeNode node = (RepositoryTreeNode) element; + switch (node.getType()) { + case REPO: + File directory = ((Repository) node.getObject()).getDirectory() + .getParentFile(); + return (directory.getName() + " - " + directory //$NON-NLS-1$ + .getAbsolutePath()); + case BRANCHES: + return RepositoryViewUITexts.RepositoriesView_Branches_Nodetext; + case PROJECTS: + return RepositoryViewUITexts.RepositoriesView_ExistingProjects_Nodetext; + case REF: + Ref ref = (Ref) node.getObject(); + // shorten the name + String refName = node.getRepository().shortenRefName( + ref.getName()); + if (ref.isSymbolic()) { + refName = refName + + " - " //$NON-NLS-1$ + + node.getRepository().shortenRefName( + ref.getLeaf().getName()); + } + return refName; + case PROJ: + + File file = (File) node.getObject(); + return file.getName(); + + default: + return null; + } + } + + public Image decorateImage(final Image image, Object element) { + + RepositoryTreeNode node = (RepositoryTreeNode) element; + switch (node.getType()) { + + case REF: + Ref ref = (Ref) node.getObject(); + // shorten the name + String refName = node.getRepository().shortenRefName( + ref.getName()); + try { + String branch = node.getBranch(); + if (refName.equals(branch)) { + CompositeImageDescriptor cd = new CompositeImageDescriptor() { + + @Override + protected Point getSize() { + return new Point(image.getBounds().width, image + .getBounds().width); + } + + @Override + protected void drawCompositeImage(int width, + int height) { + drawImage(image.getImageData(), 0, 0); + drawImage(CHECKEDOUT_OVERLAY.getImageData(), 0, + 0); + + } + }; + return cd.createImage(); + } + } catch (IOException e1) { + // simply ignore here + } + return image; + + case PROJ: + + File file = (File) node.getObject(); + + for (IProject proj : ResourcesPlugin.getWorkspace().getRoot() + .getProjects()) { + if (proj.getLocation().equals( + new Path(file.getAbsolutePath()))) { + CompositeImageDescriptor cd = new CompositeImageDescriptor() { + + @Override + protected Point getSize() { + return new Point(image.getBounds().width, image + .getBounds().width); + } + + @Override + protected void drawCompositeImage(int width, + int height) { + drawImage(image.getImageData(), 0, 0); + drawImage(CHECKEDOUT_OVERLAY.getImageData(), 0, + 0); + + } + }; + return cd.createImage(); + } + } + return image; + + default: + return image; + } + } + + } + + private List getGitDirs() { + List resultStrings = new ArrayList(); + String dirs = new InstanceScope().getNode(Activator.getPluginId()).get( + PREFS_DIRECTORIES, ""); //$NON-NLS-1$ + if (dirs != null && dirs.length() > 0) { + StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator); + while (tok.hasMoreTokens()) { + String dirName = tok.nextToken(); + File testFile = new File(dirName); + if (testFile.exists()) { + resultStrings.add(dirName); + } + } + } + Collections.sort(resultStrings); + return resultStrings; + } + + private void removeDir(String dir) { + + IEclipsePreferences prefs = new InstanceScope().getNode(Activator + .getPluginId()); + + TreeSet resultStrings = new TreeSet(); + String dirs = prefs.get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$ + if (dirs != null && dirs.length() > 0) { + StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator); + while (tok.hasMoreTokens()) { + String dirName = tok.nextToken(); + File testFile = new File(dirName); + if (testFile.exists()) { + resultStrings.add(dirName); + } + } + } + + if (resultStrings.remove(dir)) { + StringBuilder sb = new StringBuilder(); + for (String gitDirString : resultStrings) { + sb.append(gitDirString); + sb.append(File.pathSeparatorChar); + } + + prefs.put(PREFS_DIRECTORIES, sb.toString()); + try { + prefs.flush(); + } catch (BackingStoreException e) { + IStatus error = new Status(IStatus.ERROR, Activator + .getPluginId(), e.getMessage(), e); + Activator.getDefault().getLog().log(error); + } + } + + } + + @Override + public Object getAdapter(Class adapter) { + return super.getAdapter(adapter); + } + + @Override + public void createPartControl(Composite parent) { + + Composite main = new Composite(parent, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).applyTo(main); + main.setLayout(new GridLayout(1, false)); + + tv = new TreeViewer(main); + tv.setContentProvider(new ContentProvider()); + new LabelProvider(tv); + + GridDataFactory.fillDefaults().grab(true, true).applyTo(tv.getTree()); + + addContextMenu(); + + addActionsToToolbar(); + + scheduleRefresh(); + } + + private void addContextMenu() { + tv.getTree().addMenuDetectListener(new MenuDetectListener() { + + public void menuDetected(MenuDetectEvent e) { + + tv.getTree().setMenu(null); + Menu men = new Menu(tv.getTree()); + + TreeItem testItem = tv.getTree().getItem( + tv.getTree().toControl(new Point(e.x, e.y))); + if (testItem == null) { + addMenuItemsForPanel(men); + } else { + addMenuItemsForTreeSelection(men); + if (men.getItemCount() > 0) + new MenuItem(men, SWT.SEPARATOR); + addMenuItemsForPanel(men); + } + + tv.getTree().setMenu(men); + } + }); + } + + private void addMenuItemsForPanel(Menu men) { + + MenuItem importItem = new MenuItem(men, SWT.PUSH); + importItem + .setText(RepositoryViewUITexts.RepositoriesView_ImportRepository_MenuItem); + importItem.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + importAction.run(); + } + + }); + + MenuItem addItem = new MenuItem(men, SWT.PUSH); + addItem + .setText(RepositoryViewUITexts.RepositoriesView_AddRepository_MenuItem); + addItem.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + addAction.run(); + } + + }); + + MenuItem refreshItem = new MenuItem(men, SWT.PUSH); + refreshItem.setText(refreshAction.getText()); + refreshItem.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + refreshAction.run(); + } + + }); + + } + + @SuppressWarnings("unchecked") + private void addMenuItemsForTreeSelection(Menu men) { + final List> refs = new ArrayList>(); + final List> projects = new ArrayList>(); + final List> repos = new ArrayList>(); + + TreeItem[] selectedItems = tv.getTree().getSelection(); + for (TreeItem item : selectedItems) { + RepositoryTreeNode node = (RepositoryTreeNode) item.getData(); + switch (node.getType()) { + case PROJ: + projects.add(node); + break; + case REF: + refs.add(node); + break; + case REPO: + repos.add(node); + break; + default: + break; + } + } + + boolean importableProjectsOnly = !projects.isEmpty() && repos.isEmpty() + && refs.isEmpty(); + + for (RepositoryTreeNode prj : projects) { + if (!importableProjectsOnly) + break; + + for (IProject proj : ResourcesPlugin.getWorkspace().getRoot() + .getProjects()) { + if (proj.getLocation().equals( + new Path(prj.getObject().getAbsolutePath()))) + importableProjectsOnly = false; + + } + + } + + boolean singleRef = refs.size() == 1 && projects.isEmpty() + && repos.isEmpty(); + boolean singleRepo = repos.size() == 1 && projects.isEmpty() + && refs.isEmpty(); + + try { + singleRef = singleRef + && !refs.get(0).getObject().getName() + .equals(Constants.HEAD) + && (refs.get(0).getRepository().mapCommit( + refs.get(0).getObject().getLeaf().getObjectId()) != null); + } catch (IOException e2) { + singleRef = false; + } + + if (importableProjectsOnly) { + MenuItem sync = new MenuItem(men, SWT.PUSH); + sync + .setText(RepositoryViewUITexts.RepositoriesView_ImportProject_MenuItem); + + sync.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + + IWorkspaceRunnable wsr = new IWorkspaceRunnable() { + + public void run(IProgressMonitor monitor) + throws CoreException { + + for (RepositoryTreeNode projectNode : projects) { + File file = projectNode.getObject(); + + IProjectDescription pd = ResourcesPlugin + .getWorkspace().newProjectDescription( + file.getName()); + IPath locationPath = new Path(file + .getAbsolutePath()); + + pd.setLocation(locationPath); + + ResourcesPlugin.getWorkspace().getRoot() + .getProject(pd.getName()).create(pd, + monitor); + IProject project = ResourcesPlugin + .getWorkspace().getRoot().getProject( + pd.getName()); + project.open(monitor); + + File gitDir = projectNode.getRepository() + .getDirectory(); + + ConnectProviderOperation connectProviderOperation = new ConnectProviderOperation( + project, gitDir); + connectProviderOperation + .run(new SubProgressMonitor(monitor, 20)); + + } + + } + }; + + try { + + ResourcesPlugin.getWorkspace().run(wsr, + ResourcesPlugin.getWorkspace().getRoot(), + IWorkspace.AVOID_UPDATE, + new NullProgressMonitor()); + + scheduleRefresh(); + } catch (CoreException e1) { + Activator.getDefault().getLog().log(e1.getStatus()); + } + + } + + }); + } + + if (singleRef) { + + MenuItem checkout = new MenuItem(men, SWT.PUSH); + checkout + .setText(RepositoryViewUITexts.RepositoriesView_CheckOut_MenuItem); + checkout.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + Repository repo = refs.get(0).getRepository(); + String refName = refs.get(0).myObject.getLeaf().getName(); + final BranchOperation op = new BranchOperation(repo, + refName); + IWorkspaceRunnable wsr = new IWorkspaceRunnable() { + + public void run(IProgressMonitor monitor) + throws CoreException { + op.run(monitor); + } + }; + try { + ResourcesPlugin.getWorkspace().run(wsr, + ResourcesPlugin.getWorkspace().getRoot(), + IWorkspace.AVOID_UPDATE, + new NullProgressMonitor()); + scheduleRefresh(); + } catch (CoreException e1) { + MessageDialog + .openError( + getSite().getShell(), + RepositoryViewUITexts.RepositoriesView_Error_WindowTitle, + e1.getMessage()); + } + + } + + }); + } + + if (singleRepo) { + + MenuItem importProjects = new MenuItem(men, SWT.PUSH); + importProjects + .setText(RepositoryViewUITexts.RepositoriesView_ImportExistingProjects_MenuItem); + importProjects.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + Wizard wiz = new ExternalProjectImportWizard(repos.get(0) + .getRepository().getWorkDir().getAbsolutePath()) { + + @Override + public void addPages() { + super.addPages(); + // we could add some page with a single + // checkbox to indicate if we wan + // addPage(new WizardPage("Share") { + // + // public void createControl( + // Composite parent) { + // Composite main = new Composite( + // parent, SWT.NONE); + // main.setLayout(new GridLayout(1, + // false)); + // GridDataFactory.fillDefaults() + // .grab(true, true).applyTo( + // main); + // Button but = new Button(main, + // SWT.PUSH); + // but.setText("Push me"); + // setControl(main); + // + // } + // }); + } + + @Override + public boolean performFinish() { + + final Set previousLocations = new HashSet(); + // we want to share only new projects + for (IProject project : ResourcesPlugin + .getWorkspace().getRoot().getProjects()) { + previousLocations.add(project.getLocation()); + } + + boolean success = super.performFinish(); + if (success) { + // IWizardPage page = getPage("Share"); + // TODO evaluate checkbox or such, but + // if we do share + // always, we don't even need another + // page + + IWorkspaceRunnable wsr = new IWorkspaceRunnable() { + + public void run(IProgressMonitor monitor) + throws CoreException { + File gitDir = repos.get(0) + .getRepository().getDirectory(); + File gitWorkDir = repos.get(0) + .getRepository().getWorkDir(); + Path workPath = new Path(gitWorkDir + .getAbsolutePath()); + + // we check which projects are + // in the workspace + // pointing to a location in the + // repo's + // working directory + // and share them + for (IProject prj : ResourcesPlugin + .getWorkspace().getRoot() + .getProjects()) { + + if (workPath.isPrefixOf(prj + .getLocation())) { + if (previousLocations + .contains(prj + .getLocation())) { + continue; + } + ConnectProviderOperation connectProviderOperation = new ConnectProviderOperation( + prj, gitDir); + connectProviderOperation + .run(new SubProgressMonitor( + monitor, 20)); + + } + } + + } + }; + + try { + ResourcesPlugin.getWorkspace().run( + wsr, + ResourcesPlugin.getWorkspace() + .getRoot(), + IWorkspace.AVOID_UPDATE, + new NullProgressMonitor()); + scheduleRefresh(); + } catch (CoreException ce) { + MessageDialog + .openError( + getShell(), + RepositoryViewUITexts.RepositoriesView_Error_WindowTitle, + ce.getMessage()); + } + + } + return success; + } + + }; + + WizardDialog dlg = new WizardDialog(getSite().getShell(), + wiz); + dlg.open(); + } + + }); + + // TODO "import existing plug-in" menu item + // TODO "configure" menu item + + MenuItem remove = new MenuItem(men, SWT.PUSH); + remove + .setText(RepositoryViewUITexts.RepositoriesView_Remove_MenuItem); + remove.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + + List projectsToDelete = new ArrayList(); + File workDir = repos.get(0).getRepository().getWorkDir(); + final IPath wdPath = new Path(workDir.getAbsolutePath()); + for (IProject prj : ResourcesPlugin.getWorkspace() + .getRoot().getProjects()) { + if (wdPath.isPrefixOf(prj.getLocation())) { + projectsToDelete.add(prj); + } + } + + if (!projectsToDelete.isEmpty()) { + boolean confirmed; + confirmed = confirmProjectDeletion(projectsToDelete); + if (!confirmed) { + return; + } + } + + IWorkspaceRunnable wsr = new IWorkspaceRunnable() { + + public void run(IProgressMonitor monitor) + throws CoreException { + + for (IProject prj : ResourcesPlugin.getWorkspace() + .getRoot().getProjects()) { + if (wdPath.isPrefixOf(prj.getLocation())) { + prj.delete(false, false, monitor); + } + } + + Repository repo = repos.get(0).getRepository(); + removeDir(repo.getDirectory().getAbsolutePath()); + scheduleRefresh(); + } + }; + + try { + ResourcesPlugin.getWorkspace().run(wsr, + ResourcesPlugin.getWorkspace().getRoot(), + IWorkspace.AVOID_UPDATE, + new NullProgressMonitor()); + } catch (CoreException e1) { + Activator.getDefault().getLog().log(e1.getStatus()); + } + + } + + }); + + // TODO delete does not work because of file locks on .pack-files + // Shawn Pearce has added the following thoughts: + + // Hmm. We probably can't active detect file locks on pack files on + // Windows, can we? + // It would be nice if we could support a delete, but only if the + // repository is + // reasonably believed to be not-in-use right now. + // + // Within EGit you might be able to check GitProjectData and its + // repositoryCache to + // see if the repository is open by this workspace. If it is, then + // we know we shouldn't + // try to delete it. + // + // Some coding might look like this: + // + // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH); + // deleteRepo.setText("Delete"); + // deleteRepo.addSelectionListener(new SelectionAdapter() { + // + // @Override + // public void widgetSelected(SelectionEvent e) { + // + // boolean confirmed = MessageDialog.openConfirm(getSite() + // .getShell(), "Confirm", + // "This will delete the repository, continue?"); + // + // if (!confirmed) + // return; + // + // IWorkspaceRunnable wsr = new IWorkspaceRunnable() { + // + // public void run(IProgressMonitor monitor) + // throws CoreException { + // File workDir = repos.get(0).getRepository() + // .getWorkDir(); + // + // File gitDir = repos.get(0).getRepository() + // .getDirectory(); + // + // IPath wdPath = new Path(workDir.getAbsolutePath()); + // for (IProject prj : ResourcesPlugin.getWorkspace() + // .getRoot().getProjects()) { + // if (wdPath.isPrefixOf(prj.getLocation())) { + // prj.delete(false, false, monitor); + // } + // } + // + // repos.get(0).getRepository().close(); + // + // boolean deleted = deleteRecursively(gitDir, monitor); + // if (!deleted) { + // MessageDialog.openError(getSite().getShell(), + // "Error", + // "Could not delete Git Repository"); + // } + // + // deleted = deleteRecursively(workDir, monitor); + // if (!deleted) { + // MessageDialog + // .openError(getSite().getShell(), + // "Error", + // "Could not delete Git Working Directory"); + // } + // + // scheduleRefresh(); + // } + // + // private boolean deleteRecursively(File fileToDelete, + // IProgressMonitor monitor) { + // if (fileToDelete.isDirectory()) { + // for (File file : fileToDelete.listFiles()) { + // if (!deleteRecursively(file, monitor)) { + // return false; + // } + // } + // } + // monitor.setTaskName(fileToDelete.getAbsolutePath()); + // boolean deleted = fileToDelete.delete(); + // if (!deleted) { + // System.err.println("Could not delete " + // + fileToDelete.getAbsolutePath()); + // } + // return deleted; + // } + // }; + // + // try { + // ResourcesPlugin.getWorkspace().run(wsr, + // ResourcesPlugin.getWorkspace().getRoot(), + // IWorkspace.AVOID_UPDATE, + // new NullProgressMonitor()); + // } catch (CoreException e1) { + // // TODO Exception handling + // e1.printStackTrace(); + // } + // + // } + // + // }); + } + } + + private void addActionsToToolbar() { + importAction = new Action( + RepositoryViewUITexts.RepositoriesView_Import_Button) { + + @Override + public void run() { + GitCloneWizard wiz = new GitCloneWizard(); + wiz.init(null, null); + new WizardDialog(getSite().getShell(), wiz).open(); + updateDirStrings(new NullProgressMonitor()); + } + }; + importAction + .setToolTipText(RepositoryViewUITexts.RepositoriesView_Clone_Tooltip); + + getViewSite().getActionBars().getToolBarManager().add(importAction); + + addAction = new Action( + RepositoryViewUITexts.RepositoriesView_Add_Button) { + + @Override + public void run() { + RepositorySearchDialog sd = new RepositorySearchDialog( + getSite().getShell(), ResourcesPlugin.getWorkspace() + .getRoot().getLocation().toOSString(), + getGitDirs()); + if (sd.open() == Window.OK) { + Set dirs = new HashSet(); + dirs.addAll(getGitDirs()); + if (dirs.addAll(sd.getDirectories())) + saveDirStrings(dirs); + scheduleRefresh(); + } + + } + }; + addAction + .setToolTipText(RepositoryViewUITexts.RepositoriesView_AddRepository_Tooltip); + + getViewSite().getActionBars().getToolBarManager().add(addAction); + + // TODO if we don't show projects, then we probably don't need refresh + + refreshAction = new Action( + RepositoryViewUITexts.RepositoriesView_Refresh_Button) { + + @Override + public void run() { + scheduleRefresh(); + } + }; + + getViewSite().getActionBars().getToolBarManager().add(refreshAction); + } + + @Override + public void dispose() { + // make sure to cancel the refresh job + if (this.scheduledJob != null) { + this.scheduledJob.cancel(); + this.scheduledJob = null; + } + super.dispose(); + } + + private void scheduleRefresh() { + + Job job = new Job("Refreshing Git Repositories view") { //$NON-NLS-1$ + + @Override + protected IStatus run(IProgressMonitor monitor) { + + final List input; + try { + input = getRepositoriesFromDirs(monitor); + } catch (InterruptedException e) { + return new Status(IStatus.ERROR, Activator.getPluginId(), e + .getMessage(), e); + } + + Display.getDefault().syncExec(new Runnable() { + + public void run() { + // keep expansion state and selection so that we can + // restore the tree + // after update + Object[] expanded = tv.getExpandedElements(); + IStructuredSelection sel = (IStructuredSelection) tv + .getSelection(); + tv.setInput(input); + tv.setExpandedElements(expanded); + + Object selected = sel.getFirstElement(); + if (selected != null) + tv.reveal(selected); + } + }); + + return new Status(IStatus.OK, Activator.getPluginId(), ""); //$NON-NLS-1$ + + } + + }; + job.setSystem(true); + + IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService) getSite() + .getService(IWorkbenchSiteProgressService.class); + + service.schedule(job); + + scheduledJob = job; + + } + + private List getRepositoriesFromDirs(IProgressMonitor monitor) + throws InterruptedException { + + List gitDirStrings = getGitDirs(); + List input = new ArrayList(); + for (String dirString : gitDirStrings) { + if (monitor.isCanceled()) { + throw new InterruptedException( + RepositoryViewUITexts.RepositoriesView_ActionCanceled_Message); + } + try { + File dir = new File(dirString); + if (dir.exists() && dir.isDirectory()) { + input.add(new Repository(dir)); + } + } catch (IOException e) { + IStatus error = new Status(IStatus.ERROR, Activator + .getPluginId(), e.getMessage(), e); + Activator.getDefault().getLog().log(error); + } + } + return input; + } + + private void updateDirStrings(IProgressMonitor monitor) { + + IPath path = ResourcesPlugin.getWorkspace().getRoot().getLocation(); + File root = path.toFile(); + TreeSet dirStrings = new TreeSet(); + recurseDir(root, dirStrings, monitor); + saveDirStrings(dirStrings); + scheduleRefresh(); + + } + + private void saveDirStrings(Set gitDirStrings) { + StringBuilder sb = new StringBuilder(); + for (String gitDirString : gitDirStrings) { + sb.append(gitDirString); + sb.append(File.pathSeparatorChar); + } + + IEclipsePreferences prefs = new InstanceScope().getNode(Activator + .getPluginId()); + prefs.put(PREFS_DIRECTORIES, sb.toString()); + try { + prefs.flush(); + } catch (BackingStoreException e) { + IStatus error = new Status(IStatus.ERROR, Activator.getPluginId(), + e.getMessage(), e); + Activator.getDefault().getLog().log(error); + } + } + + /** + * + * @param root + * @param strings + * @param monitor + */ + public static void recurseDir(File root, TreeSet strings, + IProgressMonitor monitor) { + + if (!root.exists() || !root.isDirectory()) { + return; + } + File[] children = root.listFiles(); + for (File child : children) { + if (monitor.isCanceled()) { + return; + } + + if (child.exists() && child.isDirectory() + && RepositoryCache.FileKey.isGitRepository(child)) { + strings.add(child.getAbsolutePath()); + return; + } + if (child.isDirectory()) { + monitor.setTaskName(child.getPath()); + recurseDir(child, strings, monitor); + } + } + + } + + private static String getRepositoryName(Repository repository) { + return repository.getDirectory().getParentFile().getName(); + } + + @Override + public void setFocus() { + // nothing special + } + + @SuppressWarnings("boxing") + private boolean confirmProjectDeletion(List projectsToDelete) { + boolean confirmed; + confirmed = MessageDialog + .openConfirm( + getSite().getShell(), + RepositoryViewUITexts.RepositoriesView_ConfirmProjectDeletion_WindowTitle, + NLS + .bind( + RepositoryViewUITexts.RepositoriesView_ConfirmProjectDeletion_Question, + projectsToDelete.size())); + return confirmed; + } + +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositorySearchDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositorySearchDialog.java new file mode 100644 index 00000000..2b18e535 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositorySearchDialog.java @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright (c) 2010 SAP AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mathias Kinzler (SAP AG) - initial implementation + *******************************************************************************/ +package org.eclipse.egit.ui.internal.repository; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.BaseLabelProvider; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; + +/** + * Searches for Git directories under a path that can be selected by the user + * TODO String externalization + */ +public class RepositorySearchDialog extends Dialog { + + private final Set existingRepositoryDirs = new HashSet(); + + private final String myInitialPath; + + private Set result; + + CheckboxTableViewer tv; + + private final class ContentProvider implements IStructuredContentProvider { + + @SuppressWarnings("unchecked") + public Object[] getElements(Object inputElement) { + return ((Set) inputElement).toArray(); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // nothing + } + + public void dispose() { + // nothing + } + + } + + private final class LabelProvider extends BaseLabelProvider implements + ITableLabelProvider, IColorProvider { + + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + public String getColumnText(Object element, int columnIndex) { + return element.toString(); + } + + public Color getBackground(Object element) { + return null; + } + + public Color getForeground(Object element) { + if (existingRepositoryDirs.contains(element)) + return getShell().getDisplay().getSystemColor(SWT.COLOR_GRAY); + + return null; + } + + } + + /** + * @param parentShell + * @param initialPath + * the initial path + * @param existingDirs + */ + protected RepositorySearchDialog(Shell parentShell, String initialPath, + Collection existingDirs) { + super(parentShell); + this.existingRepositoryDirs.addAll(existingDirs); + this.myInitialPath = initialPath; + setShellStyle(getShellStyle() | SWT.SHELL_TRIM); + } + + /** + * + * @return the directories + */ + public Set getDirectories() { + return result; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText("Search Git Repositories"); + } + + @Override + protected void okPressed() { + result = new HashSet(); + Object[] checked = tv.getCheckedElements(); + for (Object o : checked) { + result.add((String) o); + } + super.okPressed(); + } + + @Override + protected Control createDialogArea(Composite parent) { + + Composite main = new Composite(parent, SWT.NONE); + main.setLayout(new GridLayout(3, false)); + + GridDataFactory.fillDefaults().grab(true, true).applyTo(main); + + Label dirLabel = new Label(main, SWT.NONE); + dirLabel.setText("Directory"); + final Text dir = new Text(main, SWT.NONE); + if (myInitialPath != null) + dir.setText(myInitialPath); + + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, + false).applyTo(dir); + + Button browse = new Button(main, SWT.PUSH); + browse.setText("Browse..."); + browse.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + DirectoryDialog dd = new DirectoryDialog(getShell()); + dd.setFilterPath(dir.getText()); + String directory = dd.open(); + if (directory != null) { + dir.setText(directory); + } + } + + }); + + Button search = new Button(main, SWT.PUSH); + search.setText("Search"); + GridDataFactory.fillDefaults().align(SWT.LEAD, SWT.CENTER).span(3, 1) + .applyTo(search); + + tv = CheckboxTableViewer.newCheckList(main, SWT.NONE); + Table tab = tv.getTable(); + GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(tab); + + tv.addCheckStateListener(new ICheckStateListener() { + + public void checkStateChanged(CheckStateChangedEvent event) { + if (existingRepositoryDirs.contains(event.getElement())) + event.getCheckable().setChecked(event.getElement(), false); + getButton(IDialogConstants.OK_ID).setEnabled( + tv.getCheckedElements().length > 0); + } + }); + + tv.setContentProvider(new ContentProvider()); + tv.setLabelProvider(new LabelProvider()); + + search.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + final TreeSet directories = new TreeSet(); + final File file = new File(dir.getText()); + if (file.exists()) { + IRunnableWithProgress action = new IRunnableWithProgress() { + + public void run(IProgressMonitor monitor) + throws InvocationTargetException, + InterruptedException { + RepositoriesView.recurseDir(file, directories, + monitor); + } + }; + try { + ProgressMonitorDialog pd = new ProgressMonitorDialog( + getShell()); + pd.run(true, true, action); + + } catch (InvocationTargetException e1) { + MessageDialog.openError(getShell(), "Error", e1 + .getCause().getMessage()); + } catch (InterruptedException e1) { + // ignore + } + + tv.setInput(directories); + } + } + + }); + + return main; + } + + @Override + protected Control createButtonBar(Composite parent) { + // disable the OK button until the user selects something + Control bar = super.createButtonBar(parent); + getButton(IDialogConstants.OK_ID).setEnabled(false); + return bar; + } + +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoryViewUITexts.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoryViewUITexts.java new file mode 100644 index 00000000..0322cc70 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/RepositoryViewUITexts.java @@ -0,0 +1,72 @@ +package org.eclipse.egit.ui.internal.repository; + +import org.eclipse.osgi.util.NLS; +/** + * UI Texts for the Repositories View + * + */ +public class RepositoryViewUITexts extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.egit.ui.internal.repository.repositoryviewuitexts"; //$NON-NLS-1$ + + /** */ + public static String RepositoriesView_ActionCanceled_Message; + + /** */ + public static String RepositoriesView_Add_Button; + + /** */ + public static String RepositoriesView_AddRepository_MenuItem; + + /** */ + public static String RepositoriesView_AddRepository_Tooltip; + + /** */ + public static String RepositoriesView_Branches_Nodetext; + + /** */ + public static String RepositoriesView_Checking_Message; + + /** */ + public static String RepositoriesView_CheckOut_MenuItem; + + /** */ + public static String RepositoriesView_Clone_Tooltip; + + /** */ + public static String RepositoriesView_ConfirmProjectDeletion_Question; + + /** */ + public static String RepositoriesView_ConfirmProjectDeletion_WindowTitle; + + /** */ + public static String RepositoriesView_Error_WindowTitle; + + /** */ + public static String RepositoriesView_ExistingProjects_Nodetext; + + /** */ + public static String RepositoriesView_Import_Button; + + /** */ + public static String RepositoriesView_ImportExistingProjects_MenuItem; + + /** */ + public static String RepositoriesView_ImportProject_MenuItem; + + /** */ + public static String RepositoriesView_ImportRepository_MenuItem; + + /** */ + public static String RepositoriesView_Refresh_Button; + + /** */ + public static String RepositoriesView_Remove_MenuItem; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, RepositoryViewUITexts.class); + } + + private RepositoryViewUITexts() { + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/repositoryviewuitexts.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/repositoryviewuitexts.properties new file mode 100644 index 00000000..e8e369c7 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/repositoryviewuitexts.properties @@ -0,0 +1,18 @@ +RepositoriesView_ActionCanceled_Message=Action was canceled +RepositoriesView_Add_Button=Add... +RepositoriesView_AddRepository_MenuItem=Add Git Repository... +RepositoriesView_AddRepository_Tooltip=Add an existing Git Repository +RepositoriesView_Branches_Nodetext=Branches +RepositoriesView_Checking_Message=Checking: {0} +RepositoriesView_CheckOut_MenuItem=Check out +RepositoriesView_Clone_Tooltip=Import (clone) a Git Repository +RepositoriesView_ConfirmProjectDeletion_Question={0} projects must be deleted, continue? +RepositoriesView_ConfirmProjectDeletion_WindowTitle=Confirm project deletion +RepositoriesView_Error_WindowTitle=Error +RepositoriesView_ExistingProjects_Nodetext=Existing Projects +RepositoriesView_Import_Button=Import... +RepositoriesView_ImportExistingProjects_MenuItem=Import Existing projects... +RepositoriesView_ImportProject_MenuItem=Import +RepositoriesView_ImportRepository_MenuItem=Import Git Repository... +RepositoriesView_Refresh_Button=Refresh +RepositoriesView_Remove_MenuItem=Remove -- 2.11.4.GIT