Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.mylyn.gerrit.ui/src')
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java114
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritImages.java48
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritRepositoryLocationUi.java138
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritRepositorySettingsPage.java229
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritReviewBehavior.java54
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java76
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/AbstractGerritSection.java87
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ComparePatchSetJob.java83
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/FetchPatchSetJob.java62
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java83
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPageFactory.java62
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GetPatchSetContentJob.java65
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/PatchSetSection.java590
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewItemLabelProvider.java113
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewSection.java274
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/egit/EGitUiUtil.java101
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/AbandonDialog.java65
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/AddReviewersDialog.java83
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/GerritOperationDialog.java200
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/PublishDialog.java193
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/RestoreDialog.java65
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/SubmitDialog.java64
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/GerritCustomQueryPage.java230
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/ProjectNameContentProposal.java47
-rw-r--r--org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/ProjectNameContentProposalProvider.java98
25 files changed, 3224 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java
new file mode 100644
index 000000000..a24f7c643
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritConnectorUi.java
@@ -0,0 +1,114 @@
+/*********************************************************************
+ * Copyright (c) 2010 Sony Ericsson/ST Ericsson and others.
+ * 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:
+ * Sony Ericsson/ST Ericsson - initial API and implementation
+ * Tasktop Technologies - improvements
+ * GitHub Inc. - fixes for bug 355179
+ *********************************************************************/
+package org.eclipse.mylyn.internal.gerrit.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.ui.wizards.GerritCustomQueryPage;
+import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.mylyn.tasks.core.ITaskMapping;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.ui.AbstractRepositoryConnectorUi;
+import org.eclipse.mylyn.tasks.ui.TaskHyperlink;
+import org.eclipse.mylyn.tasks.ui.wizards.ITaskRepositoryPage;
+import org.eclipse.mylyn.tasks.ui.wizards.ITaskSearchPage;
+import org.eclipse.mylyn.tasks.ui.wizards.NewTaskWizard;
+import org.eclipse.mylyn.tasks.ui.wizards.RepositoryQueryWizard;
+
+/**
+ * Connector specific UI for Gerrit.
+ *
+ * @author Mikael Kober
+ * @author Thomas Westling
+ * @author Steffen Pingel
+ * @author Kevin Sawicki
+ */
+public class GerritConnectorUi extends AbstractRepositoryConnectorUi {
+
+ private static final Pattern PATTERN_CHANGE_ID = Pattern.compile("(?:\\W||^)(I[0-9a-f]{8}([0-9a-f]{32})?)"); //$NON-NLS-1$
+
+ @Override
+ public String getConnectorKind() {
+ return GerritConnector.CONNECTOR_KIND;
+ }
+
+ @Override
+ public IWizard getNewTaskWizard(TaskRepository taskRepository, ITaskMapping taskSelection) {
+ return new NewTaskWizard(taskRepository, taskSelection);
+ }
+
+ @Override
+ public IWizard getQueryWizard(TaskRepository repository, IRepositoryQuery query) {
+ RepositoryQueryWizard wizard = new RepositoryQueryWizard(repository);
+ wizard.addPage(new GerritCustomQueryPage(repository, "GerritQueryPage", query)); //$NON-NLS-1$
+ return wizard;
+ }
+
+ @Override
+ public ITaskRepositoryPage getSettingsPage(TaskRepository taskRepository) {
+ return new GerritRepositorySettingsPage(taskRepository);
+ }
+
+ @Override
+ public ITaskSearchPage getSearchPage(TaskRepository repository, IStructuredSelection selection) {
+ return new GerritCustomQueryPage(repository, "GerritQueryPage", null); //$NON-NLS-1$
+ }
+
+ @Override
+ public boolean hasSearchPage() {
+ return true;
+ }
+
+ @Override
+ public String getTaskKindLabel(ITask task) {
+ return "Change";
+ }
+
+ @Override
+ public ImageDescriptor getTaskKindOverlay(ITask task) {
+ return GerritImages.OVERLAY_REVIEW;
+ }
+
+ @Override
+ public IHyperlink[] findHyperlinks(final TaskRepository repository, ITask task, String text, int index,
+ int textOffset) {
+ List<IHyperlink> links = null;
+ Matcher matcher = PATTERN_CHANGE_ID.matcher(text);
+ while (matcher.find()) {
+ if (index != -1 && (index < matcher.start() || index > matcher.end())) {
+ continue;
+ }
+ if (links == null) {
+ links = new ArrayList<IHyperlink>();
+ }
+ String key = matcher.group(1);
+ if (task == null || !key.startsWith(task.getTaskKey())) {
+ int start = matcher.start(1);
+ Region region = new Region(textOffset + start, matcher.end(1) - start);
+ links.add(new TaskHyperlink(region, repository, key));
+ }
+ }
+ return links != null ? links.toArray(new IHyperlink[links.size()]) : null;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritImages.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritImages.java
new file mode 100644
index 000000000..ba8e76946
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritImages.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+/**
+ * @author Steffen Pingel
+ */
+public class GerritImages {
+
+ private static final URL baseURL = GerritUiPlugin.getDefault().getBundle().getEntry("/icons/"); //$NON-NLS-1$
+
+ public static final String T_VIEW = "eview16"; //$NON-NLS-1$
+
+ public static final ImageDescriptor OVERLAY_REVIEW = create(T_VIEW, "overlay-review.png"); //$NON-NLS-1$
+
+ private static ImageDescriptor create(String prefix, String name) {
+ try {
+ return ImageDescriptor.createFromURL(makeIconFileURL(prefix, name));
+ } catch (MalformedURLException e) {
+ return ImageDescriptor.getMissingImageDescriptor();
+ }
+ }
+
+ private static URL makeIconFileURL(String prefix, String name) throws MalformedURLException {
+ if (baseURL == null) {
+ throw new MalformedURLException();
+ }
+
+ StringBuffer buffer = new StringBuffer(prefix);
+ buffer.append('/');
+ buffer.append(name);
+ return new URL(baseURL, buffer.toString());
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritRepositoryLocationUi.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritRepositoryLocationUi.java
new file mode 100644
index 000000000..8ec17d975
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritRepositoryLocationUi.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.mylyn.commons.net.Policy;
+import org.eclipse.mylyn.commons.net.UnsupportedRequestException;
+import org.eclipse.mylyn.commons.workbench.WorkbenchUtil;
+import org.eclipse.mylyn.commons.workbench.browser.WebBrowserDialog;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.core.client.IOpenIdLocation;
+import org.eclipse.mylyn.internal.gerrit.core.client.OpenIdAuthenticationRequest;
+import org.eclipse.mylyn.internal.gerrit.core.client.OpenIdAuthenticationResponse;
+import org.eclipse.mylyn.internal.tasks.ui.TaskRepositoryLocationUi;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.LocationAdapter;
+import org.eclipse.swt.browser.LocationEvent;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @author Steffen Pingel
+ */
+public class GerritRepositoryLocationUi extends TaskRepositoryLocationUi implements IOpenIdLocation {
+
+ private static Object lock = new Object();
+
+ private static volatile int version = 1;
+
+ public GerritRepositoryLocationUi(TaskRepository taskRepository) {
+ super(taskRepository);
+ }
+
+ @Override
+ public String getProviderUrl() {
+ if (Boolean.parseBoolean(taskRepository.getProperty(GerritConnector.KEY_REPOSITORY_OPEN_ID_ENABLED))) {
+ return taskRepository.getProperty(GerritConnector.KEY_REPOSITORY_OPEN_ID_PROVIDER);
+ }
+ return null;
+ }
+
+ @Override
+ public OpenIdAuthenticationResponse requestAuthentication(OpenIdAuthenticationRequest request,
+ IProgressMonitor monitor) throws UnsupportedRequestException {
+ if (Policy.isBackgroundMonitor(monitor)) {
+ throw new UnsupportedRequestException();
+ }
+
+ final String repositoryUrl = taskRepository.getUrl();
+
+ int currentVersion = version;
+ // synchronize on a static lock to ensure that only one password dialog is displayed at a time
+ synchronized (lock) {
+ // check if the credentials changed while the thread was waiting for the lock
+ if (currentVersion != version) {
+ // another password prompt was shown, exit to try again
+ return null;
+ }
+
+ return showAuthenticationDialog(repositoryUrl, request);
+ }
+ }
+
+ private OpenIdAuthenticationResponse showAuthenticationDialog(final String repositoryUrl,
+ final OpenIdAuthenticationRequest request) {
+ final StringBuilder sb = new StringBuilder();
+ try {
+ for (Map.Entry<String, String> entry : request.getProviderArgs().entrySet()) {
+ if (sb.length() > 0) {
+ sb.append("&"); //$NON-NLS-1$
+ }
+ sb.append(URLEncoder.encode(entry.getKey(), "UTF-8")); //$NON-NLS-1$
+ sb.append("="); //$NON-NLS-1$
+ sb.append(URLEncoder.encode(entry.getValue(), "UTF-8")); //$NON-NLS-1$
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+
+ final AtomicReference<OpenIdAuthenticationResponse> result = new AtomicReference<OpenIdAuthenticationResponse>();
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ final WebBrowserDialog dialog = new WebBrowserDialog(WorkbenchUtil.getShell(), "Login", null,
+ "Login to OpenID Provider", MessageDialog.NONE, new String[] { IDialogConstants.CANCEL_LABEL },
+ 0);
+ dialog.create();
+
+ dialog.getBrowser().addLocationListener(new LocationAdapter() {
+ @Override
+ public void changing(LocationEvent event) {
+ if (event.location != null && event.location.startsWith(request.getReturnUrl())) {
+ result.set(new OpenIdAuthenticationResponse(event.location, null));
+ }
+ // alternatively check cookies since IE does not notify listeners of redirects
+ String value = Browser.getCookie(request.getCookie(), request.getCookieUrl());
+ if (value != null) {
+ result.set(new OpenIdAuthenticationResponse(event.location, value));
+ }
+ if (result.get() != null) {
+ event.doit = false;
+ // delay execution to avoid IE crash
+ dialog.getBrowser().getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (dialog.getShell() != null && !dialog.getShell().isDisposed()) {
+ dialog.close();
+ }
+ }
+ });
+ }
+ }
+ });
+
+ // navigate to login page
+ dialog.getBrowser().setUrl(request.getRequestUrl() + "?" + sb.toString());
+ dialog.open();
+ }
+ });
+ return result.get();
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritRepositorySettingsPage.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritRepositorySettingsPage.java
new file mode 100644
index 000000000..e99e30dcc
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritRepositorySettingsPage.java
@@ -0,0 +1,229 @@
+/*********************************************************************
+ * Copyright (c) 2010 Sony Ericsson/ST Ericsson and others.
+ * 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:
+ * Sony Ericsson/ST Ericsson - initial API and implementation
+ * Tasktop Technologies - improvements
+ * GitHub, Inc. - fixes for bug 354753
+ *********************************************************************/
+package org.eclipse.mylyn.internal.gerrit.ui;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.mylyn.commons.workbench.forms.CommonFormUtil;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritSystemInfo;
+import org.eclipse.mylyn.internal.tasks.core.IRepositoryConstants;
+import org.eclipse.mylyn.tasks.core.RepositoryTemplate;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.ui.wizards.AbstractRepositorySettingsPage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+
+/**
+ * Wizard page to specify Gerrit connection details.
+ *
+ * @author Mikael Kober
+ * @author Thomas Westling
+ * @author Steffen Pingel
+ */
+public class GerritRepositorySettingsPage extends AbstractRepositorySettingsPage {
+
+ public class GerritValidator extends Validator {
+
+ private GerritSystemInfo info;
+
+ final TaskRepository repository;
+
+ public GerritValidator(TaskRepository repository) {
+ this.repository = repository;
+ }
+
+ public GerritSystemInfo getInfo() {
+ return info;
+ }
+
+ @Override
+ public void run(IProgressMonitor monitor) throws CoreException {
+ GerritConnector connector = (GerritConnector) getConnector();
+ info = connector.validate(repository, monitor);
+ }
+
+ }
+
+ private class OpenIdProvider {
+
+ private final String name;
+
+ private final String url;
+
+ public OpenIdProvider(String name, String url) {
+ this.name = name;
+ this.url = url;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ }
+
+ private Button openIdButton;
+
+ private Combo openIdCombo;
+
+ private final List<OpenIdProvider> openIdProviders = new ArrayList<OpenIdProvider>();
+
+ private Label statusLabel;
+
+ public GerritRepositorySettingsPage(TaskRepository taskRepository) {
+ super("Gerrit Repository Settings", "Web based code review and project management for Git based projects.",
+ taskRepository);
+ setNeedsAnonymousLogin(true);
+ setNeedsHttpAuth(false);
+ setNeedsAdvanced(true);
+ setNeedsEncoding(false);
+ setNeedsTimeZone(false);
+ setNeedsValidation(true);
+
+ openIdProviders.add(new OpenIdProvider("Google Account", "https://www.google.com/accounts/o8/id"));
+ openIdProviders.add(new OpenIdProvider("Yahoo Account", "https://me.yahoo.com"));
+ }
+
+ @SuppressWarnings("restriction")
+ @Override
+ public void applyTo(TaskRepository repository) {
+ super.applyTo(repository);
+ repository.setProperty(GerritConnector.KEY_REPOSITORY_OPEN_ID_ENABLED,
+ Boolean.toString(openIdButton.getSelection()));
+ repository.setProperty(GerritConnector.KEY_REPOSITORY_OPEN_ID_PROVIDER, openIdCombo.getText());
+ repository.setProperty(IRepositoryConstants.PROPERTY_CATEGORY, IRepositoryConstants.CATEGORY_REVIEW);
+ repository.removeProperty(GerritConnector.KEY_REPOSITORY_ACCOUNT_ID);
+ repository.removeProperty(GerritConnector.KEY_REPOSITORY_AUTH);
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ super.createControl(parent);
+ addRepositoryTemplatesToServerUrlCombo();
+ }
+
+ @Override
+ public String getConnectorKind() {
+ return GerritConnector.CONNECTOR_KIND;
+ }
+
+ private void updateButtons() {
+ openIdCombo.setEnabled(openIdButton.getSelection());
+ }
+
+ @Override
+ protected void applyValidatorResult(Validator validator) {
+ super.applyValidatorResult(validator);
+ if (validator.getStatus() != null && validator.getStatus().isOK()) {
+ GerritValidator gerritValidator = (GerritValidator) validator;
+ statusLabel.setText(NLS.bind("Logged in as {0}.", gerritValidator.getInfo().getFullName()));
+ } else {
+ statusLabel.setText(" ");
+ }
+ statusLabel.getParent().layout();
+ }
+
+ @Override
+ protected void createAdditionalControls(Composite parent) {
+ openIdButton = new Button(parent, SWT.CHECK);
+ openIdButton.setText("OpenID Authentication");
+ openIdButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ updateButtons();
+ }
+ });
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(openIdButton);
+
+ Label providerLabel = new Label(parent, SWT.NONE);
+ providerLabel.setText("Provider:");
+
+ openIdCombo = new Combo(parent, SWT.NONE);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(openIdCombo);
+ for (OpenIdProvider provider : openIdProviders) {
+ openIdCombo.add(provider.getName());
+ }
+ openIdCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (openIdCombo.getSelectionIndex() >= 0) {
+ openIdCombo.setText(openIdProviders.get(openIdCombo.getSelectionIndex()).getUrl());
+ }
+ }
+ });
+
+ if (repository != null) {
+ openIdButton.setSelection(Boolean.parseBoolean(repository.getProperty(GerritConnector.KEY_REPOSITORY_OPEN_ID_ENABLED)));
+ String value = repository.getProperty(GerritConnector.KEY_REPOSITORY_OPEN_ID_PROVIDER);
+ openIdCombo.setText((value != null) ? value : ""); //$NON-NLS-1$
+
+ if (openIdButton.getSelection()) {
+ if (parent.getParent() instanceof ExpandableComposite) {
+ CommonFormUtil.setExpanded((ExpandableComposite) parent.getParent(), true);
+ }
+ }
+ }
+
+ updateButtons();
+ }
+
+ @Override
+ protected void createContributionControls(Composite parent) {
+ // ignore, task editor settings are not supported
+ statusLabel = new Label(parent, SWT.WRAP);
+ statusLabel.setText(" "); //$NON-NLS-1$
+ GridDataFactory.fillDefaults().indent(0, 10).grab(true, false).span(3, SWT.DEFAULT).applyTo(statusLabel);
+ }
+
+ @Override
+ protected Validator getValidator(TaskRepository repository) {
+ return new GerritValidator(repository);
+ }
+
+ @Override
+ protected boolean isValidUrl(String url) {
+ if (url.startsWith(URL_PREFIX_HTTPS) || url.startsWith(URL_PREFIX_HTTP)) {
+ try {
+ new URL(url);
+ return true;
+ } catch (MalformedURLException e) {
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void repositoryTemplateSelected(RepositoryTemplate template) {
+ repositoryLabelEditor.setStringValue(template.label);
+ setUrl(template.repositoryUrl);
+ getContainer().updateButtons();
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritReviewBehavior.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritReviewBehavior.java
new file mode 100644
index 000000000..c099ea36f
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritReviewBehavior.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.mylyn.internal.gerrit.core.GerritOperationFactory;
+import org.eclipse.mylyn.internal.gerrit.core.operations.GerritOperation;
+import org.eclipse.mylyn.internal.gerrit.core.operations.SaveDraftRequest;
+import org.eclipse.mylyn.reviews.core.model.IFileItem;
+import org.eclipse.mylyn.reviews.core.model.ILineLocation;
+import org.eclipse.mylyn.reviews.core.model.ITopic;
+import org.eclipse.mylyn.reviews.ui.ReviewBehavior;
+import org.eclipse.mylyn.tasks.core.ITask;
+
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+
+/**
+ * @author Steffen Pingel
+ */
+public class GerritReviewBehavior extends ReviewBehavior {
+
+ private final IFileItem fileItem;
+
+ public GerritReviewBehavior(ITask task, IFileItem fileItem) {
+ super(task);
+ this.fileItem = fileItem;
+ }
+
+ public GerritOperationFactory getOperationFactory() {
+ return GerritUiPlugin.getDefault().getOperationFactory();
+ }
+
+ @Override
+ public IStatus addTopic(ITopic topic, IProgressMonitor monitor) {
+ Patch.Key key = Patch.Key.parse(fileItem.getId());
+ short side = (topic.getItem() == fileItem.getBase()) ? (short) 0 : (short) 1;
+ SaveDraftRequest request = new SaveDraftRequest(key, ((ILineLocation) topic.getLocation()).getTotalMin(), side);
+ request.setMessage(topic.getDescription());
+
+ GerritOperation<PatchLineComment> operation = getOperationFactory().createSaveDraftOperation(getTask(), request);
+ return operation.run(monitor);
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java
new file mode 100644
index 000000000..af3e7f49d
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/GerritUiPlugin.java
@@ -0,0 +1,76 @@
+/*********************************************************************
+ * Copyright (c) 2010 Sony Ericsson/ST Ericsson and others.
+ * 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:
+ * Sony Ericsson/ST Ericsson - initial API and implementation
+ *********************************************************************/
+package org.eclipse.mylyn.internal.gerrit.ui;
+
+import org.eclipse.mylyn.commons.net.AbstractWebLocation;
+import org.eclipse.mylyn.internal.gerrit.core.GerritCorePlugin;
+import org.eclipse.mylyn.internal.gerrit.core.GerritOperationFactory;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.ui.TaskRepositoryLocationUiFactory;
+import org.eclipse.mylyn.tasks.ui.TasksUi;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle.
+ *
+ * @author Thomas Westling
+ */
+public class GerritUiPlugin extends AbstractUIPlugin {
+
+ public static final String PLUGIN_ID = "org.eclipse.mylyn.gerrit.ui"; //$NON-NLS-1$
+
+ private static GerritUiPlugin plugin;
+
+ private GerritOperationFactory operationFactory;
+
+ public GerritUiPlugin() {
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+
+ GerritCorePlugin.getDefault()
+ .getConnector()
+ .setTaskRepositoryLocationFactory(new TaskRepositoryLocationUiFactory() {
+ @Override
+ public AbstractWebLocation createWebLocation(TaskRepository taskRepository) {
+ // ignore
+ return new GerritRepositoryLocationUi(taskRepository);
+ }
+ });
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance.
+ *
+ * @return the shared instance
+ */
+ public static GerritUiPlugin getDefault() {
+ return plugin;
+ }
+
+ public GerritOperationFactory getOperationFactory() {
+ if (operationFactory == null) {
+ operationFactory = new GerritOperationFactory(TasksUi.getRepositoryManager());
+ }
+ return operationFactory;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/AbstractGerritSection.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/AbstractGerritSection.java
new file mode 100644
index 000000000..e07e4b033
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/AbstractGerritSection.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ * Sascha Scholz (SAP) - improvements
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient;
+import org.eclipse.mylyn.internal.gerrit.ui.operations.GerritOperationDialog;
+import org.eclipse.mylyn.internal.tasks.ui.actions.SynchronizeEditorAction;
+import org.eclipse.mylyn.internal.tasks.ui.editors.AbstractTaskEditorSection;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.mylyn.tasks.ui.TasksUi;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.events.ExpansionAdapter;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+import com.google.gerrit.common.data.GerritConfig;
+
+/**
+ * @author Steffen Pingel
+ * @author Sascha Scholz
+ */
+public abstract class AbstractGerritSection extends AbstractTaskEditorSection {
+
+ public Label addTextClient(final FormToolkit toolkit, final Section section, String text) {
+ return addTextClient(toolkit, section, text, true);
+ }
+
+ public Label addTextClient(final FormToolkit toolkit, final Section section, String text, boolean hideOnExpand) {
+ final Label label = new Label(section, SWT.NONE);
+ label.setText(" " + text); //$NON-NLS-1$
+ label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+
+ section.setTextClient(label);
+
+ if (hideOnExpand) {
+ label.setVisible(!section.isExpanded());
+ section.addExpansionListener(new ExpansionAdapter() {
+ @Override
+ public void expansionStateChanged(ExpansionEvent e) {
+ label.setVisible(!section.isExpanded());
+ }
+ });
+ }
+
+ return label;
+ }
+
+ protected Shell getShell() {
+ return getTaskEditorPage().getSite().getShell();
+ }
+
+ protected ITask getTask() {
+ return getTaskEditorPage().getTask();
+ }
+
+ protected void openOperationDialog(GerritOperationDialog dialog) {
+ if (dialog.open() == Window.OK) {
+ SynchronizeEditorAction action = new SynchronizeEditorAction();
+ action.selectionChanged(new StructuredSelection(getTaskEditorPage().getEditor()));
+ action.run();
+ }
+ }
+
+ protected GerritConfig getConfig() {
+ GerritConnector connector = (GerritConnector) TasksUi.getRepositoryConnector(getTaskData().getConnectorKind());
+ GerritClient client = connector.getClient(getTaskEditorPage().getTaskRepository());
+ return client.getGerritConfig();
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ComparePatchSetJob.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ComparePatchSetJob.java
new file mode 100644
index 000000000..c4a064ece
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ComparePatchSetJob.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeData;
+import org.eclipse.egit.ui.internal.synchronize.GitModelSynchronize;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.internal.gerrit.ui.egit.EGitUiUtil;
+
+import com.google.gerrit.reviewdb.PatchSet;
+
+public class ComparePatchSetJob extends Job {
+
+ protected final Repository repository;
+
+ protected final PatchSet target;
+
+ private final PatchSet base;
+
+ protected final RemoteConfig remote;
+
+ public ComparePatchSetJob(Repository repository, RemoteConfig remote, PatchSet base, PatchSet target) {
+ super("Comparing Patch Set");
+ this.repository = repository;
+ this.remote = remote;
+ this.base = base;
+ this.target = target;
+ }
+
+ public void openSynchronization(String baseRef, String targetRef) throws IOException {
+ GitSynchronizeData data = new GitSynchronizeData(repository, baseRef, targetRef, false);
+ GitModelSynchronize.launch(data, data.getProjects().toArray(new IResource[0]));
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ // fetch target first to retrieve parent commit
+ String targetRef = EGitUiUtil.fetchPatchSet(subMonitor, repository, remote, target).getName();
+ String baseRef;
+ if (base != null) {
+ baseRef = EGitUiUtil.fetchPatchSet(subMonitor, repository, remote, base).getName();
+ } else {
+ baseRef = fetchParent(target, subMonitor);
+ }
+ openSynchronization(baseRef, targetRef);
+ } catch (Exception e) {
+ return new Status(IStatus.ERROR, GerritUiPlugin.PLUGIN_ID, "Patch set retrieval failed", e);
+ }
+ return Status.OK_STATUS;
+ }
+
+ private String fetchParent(PatchSet patchSet, IProgressMonitor monitor) throws URISyntaxException, CoreException,
+ IOException {
+ RevCommit targetCommit = EGitUiUtil.getRevCommit(repository, patchSet);
+ RevCommit parentCommit = targetCommit.getParents()[0];
+ return parentCommit.getName();
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/FetchPatchSetJob.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/FetchPatchSetJob.java
new file mode 100644
index 000000000..525a45a42
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/FetchPatchSetJob.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ * Sascha Scholz (SAP) - improvements
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.ui.internal.commit.RepositoryCommit;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.internal.gerrit.ui.egit.EGitUiUtil;
+
+import com.google.gerrit.reviewdb.PatchSet;
+
+public class FetchPatchSetJob extends Job {
+
+ private final Repository repository;
+
+ private final RemoteConfig remote;
+
+ private final PatchSet patchSet;
+
+ private RepositoryCommit commit;
+
+ public FetchPatchSetJob(String name, Repository repository, RemoteConfig remote, PatchSet patchSet) {
+ super(name);
+ this.repository = repository;
+ this.remote = remote;
+ this.patchSet = patchSet;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ RevCommit revCommit = EGitUiUtil.fetchPatchSet(subMonitor, repository, remote, patchSet);
+ commit = new RepositoryCommit(repository, revCommit);
+ } catch (Exception e) {
+ return new Status(IStatus.ERROR, GerritUiPlugin.PLUGIN_ID, "Patch set retrieval failed", e);
+ }
+ return Status.OK_STATUS;
+ }
+
+ public RepositoryCommit getCommit() {
+ return commit;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java
new file mode 100644
index 000000000..ed45297e8
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPage.java
@@ -0,0 +1,83 @@
+/*********************************************************************
+ * Copyright (c) 2010 Sony Ericsson/ST Ericsson and others.
+ * 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:
+ * Sony Ericsson/ST Ericsson - initial API and implementation
+ *********************************************************************/
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.core.GerritTaskSchema;
+import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
+import org.eclipse.mylyn.tasks.ui.editors.AbstractAttributeEditor;
+import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage;
+import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart;
+import org.eclipse.mylyn.tasks.ui.editors.AttributeEditorFactory;
+import org.eclipse.mylyn.tasks.ui.editors.LayoutHint;
+import org.eclipse.mylyn.tasks.ui.editors.LayoutHint.ColumnSpan;
+import org.eclipse.mylyn.tasks.ui.editors.LayoutHint.RowSpan;
+import org.eclipse.mylyn.tasks.ui.editors.TaskEditor;
+import org.eclipse.mylyn.tasks.ui.editors.TaskEditorPartDescriptor;
+
+/**
+ * @author Mikael Kober
+ * @author Thomas Westling
+ */
+public class GerritTaskEditorPage extends AbstractTaskEditorPage {
+
+ public GerritTaskEditorPage(TaskEditor editor) {
+ super(editor, GerritConnector.CONNECTOR_KIND);
+ setNeedsPrivateSection(true);
+ setNeedsSubmitButton(false);
+ }
+
+ @Override
+ protected AttributeEditorFactory createAttributeEditorFactory() {
+ return new AttributeEditorFactory(getModel(), getTaskRepository(), getEditorSite()) {
+ @Override
+ public AbstractAttributeEditor createEditor(String type, TaskAttribute taskAttribute) {
+ if (taskAttribute.getId().equals(GerritTaskSchema.getDefault().CHANGE_ID.getKey())) {
+ AbstractAttributeEditor editor = super.createEditor(type, taskAttribute);
+ editor.setLayoutHint(new LayoutHint(RowSpan.SINGLE, ColumnSpan.MULTIPLE));
+ return editor;
+ }
+ return super.createEditor(type, taskAttribute);
+ }
+ };
+ }
+
+ @Override
+ protected Set<TaskEditorPartDescriptor> createPartDescriptors() {
+ Set<TaskEditorPartDescriptor> descriptors = super.createPartDescriptors();
+ for (Iterator<TaskEditorPartDescriptor> it = descriptors.iterator(); it.hasNext();) {
+ TaskEditorPartDescriptor descriptor = it.next();
+ if (PATH_ACTIONS.equals(descriptor.getPath())) {
+ it.remove();
+ }
+ if (PATH_PEOPLE.equals(descriptor.getPath())) {
+ it.remove();
+ }
+ }
+ descriptors.add(new TaskEditorPartDescriptor(ReviewSection.class.getName()) {
+ @Override
+ public AbstractTaskEditorPart createPart() {
+ return new ReviewSection();
+ }
+ });
+ descriptors.add(new TaskEditorPartDescriptor(PatchSetSection.class.getName()) {
+ @Override
+ public AbstractTaskEditorPart createPart() {
+ return new PatchSetSection();
+ }
+ });
+ return descriptors;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPageFactory.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPageFactory.java
new file mode 100644
index 000000000..da7b67e8f
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GerritTaskEditorPageFactory.java
@@ -0,0 +1,62 @@
+/*********************************************************************
+ * Copyright (c) 2010 Sony Ericsson/ST Ericsson and others.
+ * 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:
+ * Sony Ericsson/ST Ericsson - initial API and implementation
+ *********************************************************************/
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import org.eclipse.mylyn.commons.ui.CommonImages;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.tasks.ui.ITasksUiConstants;
+import org.eclipse.mylyn.tasks.ui.TasksUiImages;
+import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
+import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPageFactory;
+import org.eclipse.mylyn.tasks.ui.editors.TaskEditor;
+import org.eclipse.mylyn.tasks.ui.editors.TaskEditorInput;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.forms.editor.IFormPage;
+
+/**
+ * @author Mikael Kober
+ * @author Thomas Westling
+ * @author Steffen Pingel
+ */
+public class GerritTaskEditorPageFactory extends AbstractTaskEditorPageFactory {
+
+ @Override
+ public boolean canCreatePageFor(TaskEditorInput input) {
+ return (input.getTask().getConnectorKind().equals(GerritConnector.CONNECTOR_KIND))
+ || (TasksUiUtil.isOutgoingNewTask(input.getTask(), GerritConnector.CONNECTOR_KIND));
+ }
+
+ @Override
+ public IFormPage createPage(TaskEditor parentEditor) {
+ return new GerritTaskEditorPage(parentEditor);
+ }
+
+ @Override
+ public String[] getConflictingIds(TaskEditorInput input) {
+ return new String[] { ITasksUiConstants.ID_PAGE_PLANNING };
+ }
+
+ @Override
+ public Image getPageImage() {
+ return CommonImages.getImage(TasksUiImages.REPOSITORY_SMALL);
+ }
+
+ @Override
+ public String getPageText() {
+ return "Gerrit";
+ }
+
+ @Override
+ public int getPriority() {
+ return PRIORITY_TASK;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GetPatchSetContentJob.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GetPatchSetContentJob.java
new file mode 100644
index 000000000..ed4f53f03
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/GetPatchSetContentJob.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritException;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritPatchSetContent;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.ui.TasksUi;
+import org.eclipse.ui.statushandlers.StatusManager;
+
+import com.google.gerrit.common.data.PatchSetDetail;
+
+public class GetPatchSetContentJob extends Job {
+
+ private GerritPatchSetContent patchSetContent;
+
+ private final PatchSetDetail patchSetDetail;
+
+ private final TaskRepository repository;
+
+ public GetPatchSetContentJob(TaskRepository repository, PatchSetDetail patchSetDetail) {
+ super("Caching Patch Set Content");
+ this.repository = repository;
+ this.patchSetDetail = patchSetDetail;
+ }
+
+ public GerritPatchSetContent getPatchSetContent() {
+ return patchSetContent;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ GerritConnector connector = (GerritConnector) TasksUi.getRepositoryConnector(repository.getConnectorKind());
+ GerritClient client = connector.getClient(repository);
+ try {
+ int reviewId = patchSetDetail.getInfo().getKey().getParentKey().get();
+ patchSetContent = client.getPatchSetContent(
+ reviewId + "", patchSetDetail.getPatchSet().getId().get(), monitor); //$NON-NLS-1$
+ } catch (OperationCanceledException e) {
+ return Status.CANCEL_STATUS;
+ } catch (GerritException e) {
+ StatusManager.getManager().handle(
+ new Status(IStatus.ERROR, GerritUiPlugin.PLUGIN_ID, "Review retrieval failed", e),
+ StatusManager.LOG);
+ }
+ return Status.OK_STATUS;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/PatchSetSection.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/PatchSetSection.java
new file mode 100644
index 000000000..6d4550d3a
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/PatchSetSection.java
@@ -0,0 +1,590 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ * Sascha Scholz (SAP) - improvements
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.egit.ui.internal.clone.GitCloneWizard;
+import org.eclipse.egit.ui.internal.commit.CommitEditor;
+import org.eclipse.egit.ui.internal.fetch.FetchGerritChangeWizard;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EContentAdapter;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.mylyn.commons.workbench.WorkbenchUtil;
+import org.eclipse.mylyn.internal.gerrit.core.GerritCorePlugin;
+import org.eclipse.mylyn.internal.gerrit.core.GerritTaskSchema;
+import org.eclipse.mylyn.internal.gerrit.core.GerritUtil;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritChange;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritPatchSetContent;
+import org.eclipse.mylyn.internal.gerrit.core.client.compat.ChangeDetailX;
+import org.eclipse.mylyn.internal.gerrit.core.egit.GerritToGitMapping;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritReviewBehavior;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.internal.gerrit.ui.operations.AbandonDialog;
+import org.eclipse.mylyn.internal.gerrit.ui.operations.PublishDialog;
+import org.eclipse.mylyn.internal.gerrit.ui.operations.RestoreDialog;
+import org.eclipse.mylyn.internal.gerrit.ui.operations.SubmitDialog;
+import org.eclipse.mylyn.internal.reviews.ui.annotations.ReviewCompareAnnotationModel;
+import org.eclipse.mylyn.internal.reviews.ui.operations.ReviewCompareEditorInput;
+import org.eclipse.mylyn.reviews.core.model.IFileItem;
+import org.eclipse.mylyn.reviews.core.model.IReviewItem;
+import org.eclipse.mylyn.reviews.internal.core.model.ReviewsPackage;
+import org.eclipse.mylyn.reviews.ui.ReviewUi;
+import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.events.ExpansionAdapter;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
+import org.eclipse.ui.internal.WorkbenchImages;
+import org.eclipse.ui.statushandlers.StatusManager;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.common.data.PatchSetPublishDetail;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+
+/**
+ * @author Steffen Pingel
+ */
+public class PatchSetSection extends AbstractGerritSection {
+
+ private class CompareAction extends Action {
+
+ private final PatchSet base;
+
+ private final PatchSet target;
+
+ private final ChangeDetail changeDetail;
+
+ public CompareAction(ChangeDetail changeDetail, PatchSet base, PatchSet target) {
+ this.changeDetail = changeDetail;
+ this.base = base;
+ this.target = target;
+ }
+
+ public void fill(Menu menu) {
+ MenuItem item = new MenuItem(menu, SWT.NONE);
+ item.setText(NLS.bind("Compare with Patch Set {0}", base.getPatchSetId()));
+ item.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ run();
+ }
+ });
+ }
+
+ @Override
+ public void run() {
+ doCompareWith(changeDetail, base, target);
+ }
+
+ }
+
+ private Composite composite;
+
+ private final List<Job> jobs;
+
+ private FormToolkit toolkit;
+
+ // XXX drafts added after the publish detail was refreshed from server
+ private int addedDrafts;
+
+ public PatchSetSection() {
+ setPartName("Patch Sets");
+ jobs = new ArrayList<Job>();
+ }
+
+ @Override
+ public void dispose() {
+ for (Job job : jobs) {
+ job.cancel();
+ }
+ super.dispose();
+ }
+
+ public void updateTextClient(Section section, final PatchSetDetail patchSetDetail, boolean cachingInProgress) {
+ String message;
+
+ String time = DateFormat.getDateTimeInstance().format(patchSetDetail.getPatchSet().getCreatedOn());
+ int numComments = getNumComments(patchSetDetail);
+ if (numComments > 0) {
+ message = NLS.bind("{0}, {1} Comments", time, numComments);
+ } else {
+ message = NLS.bind("{0}", time);
+ }
+
+ if (cachingInProgress) {
+ message += " [Caching contents...]";
+ }
+
+ final Label textClientLabel = (Label) section.getTextClient();
+ textClientLabel.setText(" " + message);
+ textClientLabel.getParent().layout(true, true);
+ //textClientLabel.setVisible(cachingInProgress || !section.isExpanded());
+ }
+
+ @Override
+ public void initialize(AbstractTaskEditorPage taskEditorPage) {
+ super.initialize(taskEditorPage);
+ }
+
+ private Composite createActions(final ChangeDetail changeDetail, final PatchSetDetail patchSetDetail,
+ final PatchSetPublishDetail publishDetail, Composite composite) {
+ Composite buttonComposite = new Composite(composite, SWT.NONE);
+ RowLayout layout = new RowLayout();
+ layout.center = true;
+ layout.spacing = 10;
+ buttonComposite.setLayout(layout);
+
+ boolean canPublish = getTaskData().getAttributeMapper().getBooleanValue(
+ getTaskData().getRoot().getAttribute(GerritTaskSchema.getDefault().CAN_PUBLISH.getKey()));
+ boolean canSubmit = false;
+ if (changeDetail.getCurrentActions() != null) {
+ canSubmit = changeDetail.getCurrentActions().contains(ApprovalCategory.SUBMIT);
+ } else if (changeDetail instanceof ChangeDetailX) {
+ // Gerrit 2.2
+ canSubmit = ((ChangeDetailX) changeDetail).canSubmit();
+ }
+
+ if (canPublish) {
+ Button publishButton = toolkit.createButton(buttonComposite, "Publish Comments...", SWT.PUSH);
+ publishButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ doPublish(publishDetail);
+ }
+ });
+ }
+
+ Button fetchButton = toolkit.createButton(buttonComposite, "Fetch...", SWT.PUSH);
+ fetchButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ doFetch(changeDetail, patchSetDetail);
+ }
+ });
+
+ final Composite compareComposite = toolkit.createComposite(buttonComposite);
+ GridLayoutFactory.fillDefaults().numColumns(2).spacing(0, 0).applyTo(compareComposite);
+
+ Button compareButton = toolkit.createButton(compareComposite, "Compare With Base", SWT.PUSH);
+ compareButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ doCompareWith(changeDetail, null, patchSetDetail.getPatchSet());
+ }
+ });
+
+ if (changeDetail.getPatchSets().size() > 1) {
+ Button compareWithButton = toolkit.createButton(compareComposite, "", SWT.PUSH);
+ GridDataFactory.fillDefaults().grab(false, true).applyTo(compareWithButton);
+ compareWithButton.setImage(WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_LCL_BUTTON_MENU));
+ compareWithButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ showCompareMenu(compareComposite, changeDetail, patchSetDetail);
+ }
+
+ private void showCompareMenu(Composite compareComposite, ChangeDetail changeDetail,
+ PatchSetDetail patchSetDetail) {
+ Menu menu = new Menu(compareComposite);
+ Point p = compareComposite.getLocation();
+ p.y = p.y + compareComposite.getSize().y;
+ p = compareComposite.getParent().toDisplay(p);
+ fillCompareWithMenu(changeDetail, patchSetDetail, menu);
+ menu.setLocation(p);
+ menu.setVisible(true);
+ }
+ });
+ }
+
+ if (canSubmit) {
+ Button submitButton = toolkit.createButton(buttonComposite, "Submit", SWT.PUSH);
+ submitButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ doSubmit(patchSetDetail.getPatchSet());
+ }
+ });
+ }
+
+ if (changeDetail != null && changeDetail.isCurrentPatchSet(patchSetDetail)) {
+ if (changeDetail.canAbandon()) {
+ Button abondonButton = toolkit.createButton(buttonComposite, "Abandon...", SWT.PUSH);
+ abondonButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ doAbandon(patchSetDetail.getPatchSet());
+ }
+ });
+ } else if (changeDetail.canRestore()) {
+ Button restoreButton = toolkit.createButton(buttonComposite, "Restore...", SWT.PUSH);
+ restoreButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ doRestore(patchSetDetail.getPatchSet());
+ }
+ });
+ }
+ }
+ return buttonComposite;
+ }
+
+ void fillCompareWithMenu(ChangeDetail changeDetail, PatchSetDetail patchSetDetail, Menu menu) {
+ for (PatchSet patchSet : changeDetail.getPatchSets()) {
+ if (patchSet.getPatchSetId() != patchSetDetail.getPatchSet().getPatchSetId()) {
+ CompareAction action = new CompareAction(changeDetail, patchSet, patchSetDetail.getPatchSet());
+ action.fill(menu);
+ }
+ }
+ }
+
+ private void createSubSection(final ChangeDetail changeDetail, final PatchSetDetail patchSetDetail,
+ final PatchSetPublishDetail publishDetail, Section section) {
+ int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
+ | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT;
+ if (changeDetail.isCurrentPatchSet(patchSetDetail)) {
+ style |= ExpandableComposite.EXPANDED;
+ }
+ final Section subSection = toolkit.createSection(composite, style);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
+ subSection.setText(NLS.bind("Patch Set {0}", patchSetDetail.getPatchSet().getId().get()));
+ subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+
+ addTextClient(toolkit, subSection, "", false); //$NON-NLS-1$
+ updateTextClient(subSection, patchSetDetail, false);
+
+ if (subSection.isExpanded()) {
+ createSubSectionContents(changeDetail, patchSetDetail, publishDetail, subSection);
+ }
+ subSection.addExpansionListener(new ExpansionAdapter() {
+ @Override
+ public void expansionStateChanged(ExpansionEvent e) {
+ if (subSection.getClient() == null) {
+ createSubSectionContents(changeDetail, patchSetDetail, publishDetail, subSection);
+ }
+ }
+ });
+ }
+
+ private int getNumComments(PatchSetDetail patchSetDetail) {
+ int numComments = 0;
+ for (Patch patch : patchSetDetail.getPatches()) {
+ numComments += patch.getCommentCount();
+ }
+ return numComments;
+ }
+
+ private void subSectionExpanded(final PatchSetDetail patchSetDetail, final Section composite, final Viewer viewer) {
+ updateTextClient(composite, patchSetDetail, true);
+
+ final GetPatchSetContentJob job = new GetPatchSetContentJob(getTaskEditorPage().getTaskRepository(),
+ patchSetDetail);
+ job.addJobChangeListener(new JobChangeAdapter() {
+ @Override
+ public void done(final IJobChangeEvent event) {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ if (getControl() != null && !getControl().isDisposed()) {
+ if (event.getResult().isOK()) {
+ GerritPatchSetContent content = job.getPatchSetContent();
+ if (content != null && content.getPatchScriptByPatchKey() != null) {
+ List<IReviewItem> items = GerritUtil.toReviewItems(patchSetDetail,
+ content.getPatchScriptByPatchKey());
+ viewer.setInput(items);
+ }
+ }
+
+ updateTextClient(composite, patchSetDetail, false);
+ getTaskEditorPage().reflow();
+ }
+ }
+ });
+ }
+ });
+ jobs.add(job);
+ job.schedule();
+ }
+
+ @Override
+ protected Control createContent(FormToolkit toolkit, Composite parent) {
+ this.toolkit = toolkit;
+
+ composite = toolkit.createComposite(parent);
+ GridLayoutFactory.fillDefaults().extendedMargins(0, 0, 0, 5).applyTo(composite);
+
+ GerritChange change = GerritUtil.getChange(getTaskData());
+ if (change != null) {
+ for (PatchSetDetail patchSetDetail : change.getPatchSetDetails()) {
+ PatchSet.Id patchSetId = patchSetDetail.getPatchSet().getId();
+ PatchSetPublishDetail publishDetail = change.getPublishDetailByPatchSetId().get(patchSetId);
+ createSubSection(change.getChangeDetail(), patchSetDetail, publishDetail, getSection());
+ }
+ }
+
+ return composite;
+ }
+
+ protected void doAbandon(PatchSet patchSet) {
+ AbandonDialog dialog = new AbandonDialog(getShell(), getTask(), patchSet);
+ openOperationDialog(dialog);
+ }
+
+ protected void doPublish(PatchSetPublishDetail publishDetail) {
+ PublishDialog dialog = new PublishDialog(getShell(), getTask(), publishDetail, addedDrafts);
+ openOperationDialog(dialog);
+ }
+
+ protected void doFetch(ChangeDetail changeDetail, PatchSetDetail patchSetDetail) {
+ GerritToGitMapping mapping = getRepository(changeDetail);
+ if (mapping != null) {
+ String refName = patchSetDetail.getPatchSet().getRefName();
+ FetchGerritChangeWizard wizard = new FetchGerritChangeWizard(mapping.getRepository(), refName);
+ WizardDialog wizardDialog = new WizardDialog(getShell(), wizard);
+ wizardDialog.setHelpAvailable(false);
+ wizardDialog.open();
+ }
+ }
+
+ private GerritToGitMapping getRepository(ChangeDetail changeDetail) {
+ GerritToGitMapping mapper = new GerritToGitMapping(getTaskEditorPage().getTaskRepository(), getConfig(),
+ getGerritProject(changeDetail));
+ try {
+ if (mapper.find() != null) {
+ return mapper;
+ } else if (mapper.getGerritProject() != null) {
+ String uri = NLS.bind("{0}/p/{1}", getTaskEditorPage().getTaskRepository(), mapper.getGerritProject());
+ WizardDialog dlg = new WizardDialog(WorkbenchUtil.getShell(), new GitCloneWizard(uri));
+ dlg.setHelpAvailable(false);
+ int response = dlg.open();
+ if (response == Window.OK && mapper.find() != null) {
+ return mapper;
+ }
+ } else {
+ String message = NLS.bind("No Git repository found for fetching Gerrit change {0}",
+ getTask().getTaskKey());
+ String reason = NLS.bind(
+ "No remote config found that has fetch URL with host ''{0}'' and path matching ''{1}''",
+ mapper.getGerritHost(), mapper.getGerritProject());
+ GerritCorePlugin.logError(message, null);
+ ErrorDialog.openError(getShell(), "Gerrit Fetch Change Error", message, new Status(IStatus.ERROR,
+ GerritUiPlugin.PLUGIN_ID, reason));
+ }
+ } catch (IOException e) {
+ Status status = new Status(IStatus.ERROR, GerritUiPlugin.PLUGIN_ID, "Error accessing Git repository", e);
+ StatusManager.getManager().handle(status, StatusManager.BLOCK | StatusManager.SHOW | StatusManager.LOG);
+ }
+ return null;
+ }
+
+ protected void doCompareWith(ChangeDetail changeDetail, PatchSet base, PatchSet target) {
+ GerritToGitMapping mapping = getRepository(changeDetail);
+ if (mapping != null) {
+ ComparePatchSetJob job = new ComparePatchSetJob(mapping.getRepository(), mapping.getRemote(), base, target);
+ job.schedule();
+ }
+ }
+
+ private String getGerritProject(ChangeDetail changeDetail) {
+ return changeDetail.getChange().getProject().get();
+ }
+
+ protected void doRestore(PatchSet patchSet) {
+ RestoreDialog dialog = new RestoreDialog(getShell(), getTask(), patchSet);
+ openOperationDialog(dialog);
+ }
+
+ protected void doSubmit(PatchSet patchSet) {
+ SubmitDialog dialog = new SubmitDialog(getShell(), getTask(), patchSet);
+ openOperationDialog(dialog);
+ }
+
+ @Override
+ protected boolean shouldExpandOnCreate() {
+ return true;
+ }
+
+ void createSubSectionContents(final ChangeDetail changeDetail, final PatchSetDetail patchSetDetail,
+ PatchSetPublishDetail publishDetail, Section subSection) {
+ Composite composite = toolkit.createComposite(subSection);
+ GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite);
+ subSection.setClient(composite);
+
+ Label authorLabel = new Label(composite, SWT.NONE);
+ authorLabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ authorLabel.setText("Author");
+
+ Text authorText = new Text(composite, SWT.READ_ONLY);
+ authorText.setText(GerritUtil.getUserLabel(patchSetDetail.getInfo().getAuthor()));
+
+ Label committerLabel = new Label(composite, SWT.NONE);
+ committerLabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ committerLabel.setText("Committer");
+
+ Text committerText = new Text(composite, SWT.READ_ONLY);
+ committerText.setText(GerritUtil.getUserLabel(patchSetDetail.getInfo().getCommitter()));
+
+ Label commitLabel = new Label(composite, SWT.NONE);
+ commitLabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ commitLabel.setText("Commit");
+
+ Hyperlink commitLink = new Hyperlink(composite, SWT.READ_ONLY);
+ commitLink.setText(patchSetDetail.getPatchSet().getRevision().get());
+ commitLink.addHyperlinkListener(new HyperlinkAdapter() {
+ @Override
+ public void linkActivated(HyperlinkEvent event) {
+ GerritToGitMapping mapping = getRepository(changeDetail);
+ if (mapping != null) {
+ final FetchPatchSetJob job = new FetchPatchSetJob("Opening Commit Viewer", mapping.getRepository(),
+ mapping.getRemote(), patchSetDetail.getPatchSet());
+ job.schedule();
+ job.addJobChangeListener(new JobChangeAdapter() {
+ @Override
+ public void done(IJobChangeEvent event) {
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ CommitEditor.openQuiet(job.getCommit());
+ }
+ });
+ }
+ });
+ }
+ }
+ });
+
+ Label refLabel = new Label(composite, SWT.NONE);
+ refLabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ refLabel.setText("Ref");
+
+ Text refText = new Text(composite, SWT.READ_ONLY);
+ refText.setText(patchSetDetail.getPatchSet().getRefName());
+
+ final TableViewer viewer = new TableViewer(composite, SWT.SINGLE | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL);
+ GridDataFactory.fillDefaults().span(2, 1).grab(true, true).hint(500, SWT.DEFAULT).applyTo(viewer.getControl());
+ viewer.setContentProvider(new IStructuredContentProvider() {
+ private EContentAdapter modelAdapter;
+
+ public void dispose() {
+ // ignore
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return ((List) inputElement).toArray();
+ }
+
+ public void inputChanged(final Viewer viewer, Object oldInput, Object newInput) {
+ if (oldInput instanceof List<?> && modelAdapter != null) {
+ for (IReviewItem item : (List<IReviewItem>) oldInput) {
+ ((EObject) item).eAdapters().remove(modelAdapter);
+ }
+ addedDrafts = 0;
+ }
+
+ if (newInput instanceof List<?>) {
+ // monitors any new topics that are added
+ modelAdapter = new EContentAdapter() {
+ @Override
+ public void notifyChanged(Notification notification) {
+ super.notifyChanged(notification);
+ if (notification.getFeatureID(IReviewItem.class) == ReviewsPackage.REVIEW_ITEM__TOPICS
+ && notification.getEventType() == Notification.ADD) {
+ viewer.refresh();
+ addedDrafts++;
+ }
+ }
+ };
+ for (Object item : (List) newInput) {
+ ((EObject) item).eAdapters().add(modelAdapter);
+ }
+ }
+ }
+ });
+ viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new ReviewItemLabelProvider()));
+ viewer.addOpenListener(new IOpenListener() {
+ public void open(OpenEvent event) {
+ IStructuredSelection selection = (IStructuredSelection) event.getSelection();
+ IFileItem item = (IFileItem) selection.getFirstElement();
+
+ ReviewUi.setActiveReview(new GerritReviewBehavior(getTask(), item));
+
+ ReviewCompareAnnotationModel model = new ReviewCompareAnnotationModel(item, null);
+ CompareConfiguration configuration = new CompareConfiguration();
+ if (item.getBase() != null && item.getTarget() != null) {
+ CompareUI.openCompareEditor(new ReviewCompareEditorInput(item, model, configuration));
+ } else {
+ // the content has not been cached, yet
+ getTaskEditorPage().getEditor().setMessage("The selected file is not available, yet",
+ IMessageProvider.WARNING);
+ }
+ }
+ });
+
+ List<IReviewItem> items = GerritUtil.toReviewItems(patchSetDetail, null);
+ viewer.setInput(items);
+
+ Composite actionComposite = createActions(changeDetail, patchSetDetail, publishDetail, composite);
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(actionComposite);
+
+ subSectionExpanded(patchSetDetail, subSection, viewer);
+
+ getTaskEditorPage().reflow();
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewItemLabelProvider.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewItemLabelProvider.java
new file mode 100644
index 000000000..9b891bfde
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewItemLabelProvider.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ * Git Hub, Inc. - fixes for bug 354570
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import java.util.List;
+
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.StyledString.Styler;
+import org.eclipse.mylyn.commons.workbench.CommonImageManger;
+import org.eclipse.mylyn.reviews.core.model.IFileItem;
+import org.eclipse.mylyn.reviews.core.model.IReviewItem;
+import org.eclipse.mylyn.reviews.core.model.ITopic;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.TextStyle;
+
+/**
+ * @author Steffen Pingel
+ * @author Kevin Sawicki
+ */
+public class ReviewItemLabelProvider extends LabelProvider implements IStyledLabelProvider {
+
+ final Styler NO_STYLE = new Styler() {
+ @Override
+ public void applyStyles(TextStyle textStyle) {
+ }
+ };
+
+ private final CommonImageManger imageManager;
+
+ public ReviewItemLabelProvider() {
+ imageManager = new CommonImageManger();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ imageManager.dispose();
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ if (element instanceof IReviewItem) {
+ IReviewItem item = (IReviewItem) element;
+ return imageManager.getFileImage(item.getName());
+ }
+ return null;
+ }
+
+ @Override
+ public String getText(Object element) {
+ if (element instanceof IReviewItem) {
+ return ((IReviewItem) element).getName();
+ }
+ return super.getText(element);
+ }
+
+ public StyledString getStyledText(Object element) {
+ String text = getText(element);
+ if (text != null) {
+ StyledString styledString = new StyledString(text);
+ if (element instanceof IFileItem) {
+ IFileItem fileItem = (IFileItem) element;
+ if (fileItem.getBase() != null && fileItem.getTarget() != null) {
+ Stats stats = new Stats();
+ count(stats, fileItem.getTopics());
+ count(stats, fileItem.getBase().getTopics());
+ count(stats, fileItem.getTarget().getTopics());
+ if (stats.comments > 0 && stats.drafts > 0) {
+ styledString.append(NLS.bind(" [{0} comments, {1} drafts]", stats.comments, stats.drafts),
+ StyledString.DECORATIONS_STYLER);
+ } else if (stats.comments > 0) {
+ styledString.append(NLS.bind(" [{0} comments]", stats.comments),
+ StyledString.DECORATIONS_STYLER);
+ } else if (stats.drafts > 0) {
+ styledString.append(NLS.bind(" [{0} drafts]", stats.drafts), StyledString.DECORATIONS_STYLER);
+ }
+ }
+ }
+ return styledString;
+ }
+ return new StyledString();
+ }
+
+ private class Stats {
+ int drafts;
+
+ int comments;
+ }
+
+ private void count(Stats stats, List<ITopic> topics) {
+ for (ITopic topic : topics) {
+ if (topic.isDraft()) {
+ stats.drafts++;
+ } else {
+ stats.comments++;
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewSection.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewSection.java
new file mode 100644
index 000000000..c70eee6fb
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/editor/ReviewSection.java
@@ -0,0 +1,274 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ * Jan Lohre (SAP) - improvements
+ * Sascha Scholz (SAP) - improvements
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.editor;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.mylyn.internal.gerrit.core.GerritUtil;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritChange;
+import org.eclipse.mylyn.internal.gerrit.ui.operations.AddReviewersDialog;
+import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.ApprovalDetail;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.common.data.GerritConfig;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+
+/**
+ * @author Steffen Pingel
+ */
+public class ReviewSection extends AbstractGerritSection {
+
+ private Composite composite;
+
+ private FormToolkit toolkit;
+
+ public ReviewSection() {
+ setPartName("Review");
+ }
+
+ @Override
+ protected Control createContent(FormToolkit toolkit, Composite parent) {
+ this.toolkit = toolkit;
+
+ composite = toolkit.createComposite(parent);
+ GridLayoutFactory.fillDefaults().extendedMargins(0, 0, 0, 5).applyTo(composite);
+
+ GerritChange review = GerritUtil.getChange(getTaskData());
+ if (review != null) {
+ createReviewContent(toolkit, composite, review.getChangeDetail());
+ }
+ return composite;
+ }
+
+ private void createReviewContent(FormToolkit toolkit, Composite composite, ChangeDetail changeDetail) {
+ GerritConfig config = getConfig();
+
+ createPeopleSubSection(composite, changeDetail, config);
+
+ if (changeDetail.getMissingApprovals() != null && changeDetail.getMissingApprovals().size() > 0) {
+ createRequirementsSubSection(toolkit, composite, config, changeDetail);
+ }
+
+ if (changeDetail.getDependsOn() != null && changeDetail.getDependsOn().size() > 0) {
+ createDependenciesSubSection(toolkit, composite, "Depends On", changeDetail, changeDetail.getDependsOn());
+ }
+ if (changeDetail.getNeededBy() != null && changeDetail.getNeededBy().size() > 0) {
+ createDependenciesSubSection(toolkit, composite, "Needed By", changeDetail, changeDetail.getNeededBy());
+ }
+ }
+
+ void createPeopleSubSection(Composite parent, ChangeDetail changeDetail, GerritConfig config) {
+ if (changeDetail.getApprovals().isEmpty() && !canAddReviewers(changeDetail)) {
+ return;
+ }
+
+ int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
+ | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT;
+
+ final Section subSection = toolkit.createSection(parent, style);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
+ subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ subSection.setText("Reviewers");
+
+ List<ApprovalType> approvalTypes;
+ if (config != null && config.getApprovalTypes() != null && config.getApprovalTypes().getApprovalTypes() != null) {
+ approvalTypes = config.getApprovalTypes().getApprovalTypes();
+ } else {
+ approvalTypes = Collections.emptyList();
+ }
+
+ Composite composite = toolkit.createComposite(subSection);
+ int numColumns = approvalTypes.size() + 1;
+ GridLayoutFactory.fillDefaults()
+ .numColumns(numColumns)
+ .extendedMargins(0, 0, 0, 5)
+ .spacing(20, 5)
+ .applyTo(composite);
+ subSection.setClient(composite);
+
+ if (changeDetail.getApprovals().size() > 0) {
+ StringBuilder names = new StringBuilder();
+
+ // column headers
+ Label label = new Label(composite, SWT.NONE);
+ label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ label.setText(" "); //$NON-NLS-1$
+ for (ApprovalType approvalType : approvalTypes) {
+ label = new Label(composite, SWT.NONE);
+ label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ label.setText(approvalType.getCategory().getName());
+ }
+
+ for (ApprovalDetail approvalDetail : changeDetail.getApprovals()) {
+ AccountInfo user = changeDetail.getAccounts().get(approvalDetail.getAccount());
+
+ label = new Label(composite, SWT.NONE);
+ label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ label.setText(GerritUtil.getUserLabel(user));
+
+ for (ApprovalType approvalType : approvalTypes) {
+ PatchSetApproval approval = approvalDetail.getApprovalMap().get(approvalType.getCategory().getId());
+ if (approval != null && approval.getValue() != 0) {
+ label = new Label(composite, SWT.NONE);
+ GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(label);
+
+ ApprovalCategoryValue approvalValue = approvalType.getValue(approval.getValue());
+ if (approvalValue != null) {
+ label.setText(approvalValue.formatValue());
+ label.setToolTipText(approvalValue.format());
+ } else {
+ label.setText(approval.getValue() + ""); //$NON-NLS-1$
+ }
+ } else {
+ label = new Label(composite, SWT.NONE);
+ label.setText(" "); //$NON-NLS-1$
+ }
+ }
+
+ if (names.length() > 0) {
+ names.append(", "); //$NON-NLS-1$
+ }
+ names.append(GerritUtil.getUserLabel(user));
+ }
+
+ if (names.length() > 0) {
+ addTextClient(toolkit, subSection, names.toString());
+ }
+ }
+
+ Composite buttonComposite = new Composite(composite, SWT.NONE);
+ GridDataFactory.fillDefaults().span(numColumns, 1).applyTo(buttonComposite);
+ RowLayout layout = new RowLayout();
+ layout.center = true;
+ layout.spacing = 10;
+ buttonComposite.setLayout(layout);
+ Button addReviewersButton = toolkit.createButton(buttonComposite, "Add Reviewers...", SWT.PUSH);
+ addReviewersButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ doAddReviewers();
+ }
+ });
+ }
+
+ private void doAddReviewers() {
+ AddReviewersDialog dialog = new AddReviewersDialog(getShell(), getTask());
+ openOperationDialog(dialog);
+ }
+
+ private boolean canAddReviewers(ChangeDetail changeDetail) {
+ return changeDetail.getChange().getStatus() == Change.Status.NEW;
+ }
+
+ private void createRequirementsSubSection(final FormToolkit toolkit, final Composite parent, GerritConfig config,
+ ChangeDetail changeDetail) {
+ if (config == null) {
+ return;
+ }
+
+ int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT
+ | ExpandableComposite.LEFT_TEXT_CLIENT_ALIGNMENT;
+
+ final Section subSection = toolkit.createSection(parent, style);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
+ subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ subSection.setText("Requirements");
+
+ Composite composite = toolkit.createComposite(subSection);
+ GridLayoutFactory.fillDefaults().numColumns(2).spacing(20, 5).extendedMargins(0, 0, 0, 5).applyTo(composite);
+ subSection.setClient(composite);
+
+ StringBuilder sb = new StringBuilder();
+
+ for (ApprovalCategory.Id approvalCategoryId : changeDetail.getMissingApprovals()) {
+ ApprovalType type = config.getApprovalTypes().getApprovalType(approvalCategoryId);
+ if (type != null) {
+ ApprovalCategoryValue approvalValue = type.getMax();
+ if (approvalValue != null) {
+ Label label1 = new Label(composite, SWT.NONE);
+ label1.setText(type.getCategory().getName());
+ label1.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+
+ Label label2 = new Label(composite, SWT.NONE);
+ label2.setText(approvalValue.format());
+ //label2.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+
+ if (sb.length() > 0) {
+ sb.append(", "); //$NON-NLS-1$
+ }
+ sb.append(type.getCategory().getName());
+ }
+ }
+ }
+
+ addTextClient(toolkit, subSection, sb.toString());
+ }
+
+ private void createDependenciesSubSection(final FormToolkit toolkit, final Composite parent, String title,
+ ChangeDetail changeDetail, List<ChangeInfo> changeInfos) {
+ int style = ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT;
+
+ final Section subSection = toolkit.createSection(parent, style);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(subSection);
+ subSection.setTitleBarForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+ subSection.setText(title);
+
+ Composite composite = toolkit.createComposite(subSection);
+ GridLayoutFactory.fillDefaults().extendedMargins(0, 0, 0, 5).applyTo(composite);
+ subSection.setClient(composite);
+
+ for (final ChangeInfo changeInfo : changeInfos) {
+ AccountInfo user = changeDetail.getAccounts().get(changeInfo.getOwner());
+ Link link = new Link(composite, SWT.NONE);
+ link.setText(NLS.bind("<a>{0}</a>: {1} ({3}) by {2}", new String[] { changeInfo.getKey().abbreviate(),
+ changeInfo.getSubject(), GerritUtil.getUserLabel(user), String.valueOf(changeInfo.getStatus()) }));
+ link.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ TasksUiUtil.openTask(getTaskEditorPage().getTaskRepository(), changeInfo.getId() + ""); //$NON-NLS-1$
+ }
+ });
+ }
+ }
+
+ @Override
+ protected boolean shouldExpandOnCreate() {
+ return true;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/egit/EGitUiUtil.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/egit/EGitUiUtil.java
new file mode 100644
index 000000000..84eec7376
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/egit/EGitUiUtil.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.egit;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.UIPreferences;
+import org.eclipse.egit.ui.internal.fetch.FetchOperationUI;
+import org.eclipse.jgit.errors.AmbiguousObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.FetchResult;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.mylyn.commons.core.StatusHandler;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+
+import com.google.gerrit.reviewdb.PatchSet;
+
+/**
+ * Provides common UI utilities.
+ *
+ * @author Steffen Pingel
+ */
+public class EGitUiUtil {
+
+ private static boolean credentialsProviderWarningLogged;
+
+ public static void setCredentialsProvider(FetchOperationUI op) {
+ // TODO EGit1.1 replace with op.setCredentialsProvider(new EGitCredentialsProvider())
+ try {
+ Class clazz = Class.forName("org.eclipse.egit.ui.internal.credentials.EGitCredentialsProvider");
+ Method method = FetchOperationUI.class.getDeclaredMethod("setCredentialsProvider", clazz);
+ method.invoke(op, clazz.newInstance());
+ } catch (Exception e) {
+ if (!credentialsProviderWarningLogged) {
+ credentialsProviderWarningLogged = true;
+ StatusHandler.log(new Status(
+ IStatus.WARNING,
+ GerritUiPlugin.PLUGIN_ID,
+ "Fetch operation may fail: EGit credentials provider not available. EGit 1.1 or later is required.",
+ e));
+ }
+ }
+ }
+
+ public static RevCommit getRevCommit(Repository repository, PatchSet target) throws AmbiguousObjectException,
+ IOException, MissingObjectException, IncorrectObjectTypeException {
+ ObjectId ref = repository.resolve(target.getRevision().get());
+ RevWalk walker = new RevWalk(repository);
+ RevCommit targetCommit = walker.parseCommit(ref);
+ return targetCommit;
+ }
+
+ private static RevCommit fetchRefSpec(IProgressMonitor monitor, Repository repository, RemoteConfig remote,
+ RefSpec refSpec) throws URISyntaxException, CoreException, MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ List<RefSpec> refSpecs = Collections.singletonList(refSpec);
+ FetchOperationUI op = new FetchOperationUI(repository, remote.getURIs().get(0), refSpecs,
+ Activator.getDefault().getPreferenceStore().getInt(UIPreferences.REMOTE_CONNECTION_TIMEOUT), false);
+ EGitUiUtil.setCredentialsProvider(op);
+ FetchResult result = op.execute(monitor);
+ ObjectId resultRef = result.getAdvertisedRef(refSpec.getSource()).getObjectId();
+ return new RevWalk(repository).parseCommit(resultRef);
+ }
+
+ public static RevCommit fetchPatchSet(IProgressMonitor monitor, Repository repository, RemoteConfig remote,
+ PatchSet patchSet) throws IOException, CoreException, URISyntaxException {
+ try {
+ // commit was already fetched
+ return EGitUiUtil.getRevCommit(repository, patchSet);
+ } catch (MissingObjectException e) {
+ // need to getch it
+ RefSpec refSpec = new RefSpec(patchSet.getRefName() + ":FETCH_HEAD"); //$NON-NLS-1$
+ return fetchRefSpec(monitor, repository, remote, refSpec);
+ }
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/AbandonDialog.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/AbandonDialog.java
new file mode 100644
index 000000000..475892139
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/AbandonDialog.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.operations;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.mylyn.internal.gerrit.core.operations.AbandonRequest;
+import org.eclipse.mylyn.internal.gerrit.core.operations.GerritOperation;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.internal.tasks.ui.editors.RichTextEditor;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.reviewdb.PatchSet;
+
+/**
+ * @author Steffen Pingel
+ */
+public class AbandonDialog extends GerritOperationDialog {
+
+ private RichTextEditor messageEditor;
+
+ private final PatchSet patchSet;
+
+ public AbandonDialog(Shell parentShell, ITask task, PatchSet patchSet) {
+ super(parentShell, task);
+ this.patchSet = patchSet;
+ }
+
+ @Override
+ public GerritOperation<ChangeDetail> createOperation() {
+ int patchSetId = patchSet.getId().get();
+ AbandonRequest request = new AbandonRequest(task.getTaskId(), patchSetId);
+ request.setMessage(messageEditor.getText());
+ return GerritUiPlugin.getDefault().getOperationFactory().createAbandonOperation(task, request);
+ }
+
+ @Override
+ protected Control createPageControls(Composite parent) {
+ setTitle("Abondon Change");
+ setMessage("Enter an optional message.");
+
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(1, false));
+
+ messageEditor = createRichTextEditor(composite, "");
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(messageEditor.getControl());
+
+ return composite;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/AddReviewersDialog.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/AddReviewersDialog.java
new file mode 100644
index 000000000..076f37658
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/AddReviewersDialog.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.operations;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.mylyn.internal.gerrit.core.operations.AddReviewersRequest;
+import org.eclipse.mylyn.internal.gerrit.core.operations.GerritOperation;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import com.google.gerrit.common.data.ReviewerResult;
+
+/**
+ * @author Steffen Pingel
+ * @author Benjamin Muskalla
+ */
+public class AddReviewersDialog extends GerritOperationDialog {
+
+ private Text messageEditor;
+
+ public AddReviewersDialog(Shell parentShell, ITask task) {
+ super(parentShell, task);
+ }
+
+ @Override
+ public GerritOperation<ReviewerResult> createOperation() {
+ AddReviewersRequest request = new AddReviewersRequest(task.getTaskId(), getReviewers());
+ return GerritUiPlugin.getDefault().getOperationFactory().createAddReviewersOperation(task, request);
+ }
+
+ private List<String> getReviewers() {
+ String[] reviewers = messageEditor.getText().split(",");
+ return Arrays.asList(reviewers);
+ }
+
+ @Override
+ protected Control createPageControls(Composite parent) {
+ setTitle("Add Reviewers");
+ setMessage("Enter a comma separated list of names or email addresses.");
+
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = GridLayoutFactory.fillDefaults().margins(5, 5).create();
+ composite.setLayout(layout);
+
+ messageEditor = createPersonTextEditor(composite, "");
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(messageEditor);
+
+ return composite;
+ }
+
+ @Override
+ protected boolean processOperationResult(GerritOperation<?> operation) {
+ Object result = operation.getOperationResult();
+ if (result instanceof ReviewerResult) {
+ ReviewerResult reviewerResult = (ReviewerResult) result;
+ if (reviewerResult.getErrors() != null && reviewerResult.getErrors().size() > 0) {
+ setErrorMessage(reviewerResult.getErrors().toString());
+ return false;
+ }
+ }
+ return super.processOperationResult(operation);
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/GerritOperationDialog.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/GerritOperationDialog.java
new file mode 100644
index 000000000..222290bde
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/GerritOperationDialog.java
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ * Sascha Scholz (SAP) - improvements
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.operations;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.jface.fieldassist.TextContentAdapter;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.mylyn.internal.gerrit.core.GerritOperationFactory;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritConfiguration;
+import org.eclipse.mylyn.internal.gerrit.core.operations.GerritOperation;
+import org.eclipse.mylyn.internal.gerrit.core.operations.RefreshConfigRequest;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
+import org.eclipse.mylyn.internal.tasks.ui.editors.RichTextEditor;
+import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorExtensions;
+import org.eclipse.mylyn.reviews.ui.ProgressDialog;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.ui.ITasksUiFactory;
+import org.eclipse.mylyn.tasks.ui.TasksUi;
+import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorExtension;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.statushandlers.StatusManager;
+
+import com.google.gerrit.common.data.GerritConfig;
+
+/**
+ * @author Steffen Pingel
+ * @author Benjamin Muskalla
+ * @author Sascha Scholz
+ */
+public abstract class GerritOperationDialog extends ProgressDialog {
+
+ private boolean needsConfig;
+
+ protected final ITask task;
+
+ protected FormToolkit toolkit;
+
+ public GerritOperationDialog(Shell parentShell, ITask task) {
+ super(parentShell);
+ this.task = task;
+ }
+
+ @Override
+ public boolean close() {
+ if (getReturnCode() == OK) {
+ boolean shouldClose = performOperation(createOperation());
+ if (!shouldClose) {
+ return false;
+ }
+ }
+ return super.close();
+ }
+
+ public abstract GerritOperation<?> createOperation();
+
+ public GerritOperationFactory getOperationFactory() {
+ return GerritUiPlugin.getDefault().getOperationFactory();
+ }
+
+ public ITask getTask() {
+ return task;
+ }
+
+ public boolean needsConfig() {
+ return needsConfig;
+ }
+
+ public void setNeedsConfig(boolean needsConfig) {
+ this.needsConfig = needsConfig;
+ }
+
+ private boolean performOperation(final GerritOperation<?> operation) {
+ final AtomicReference<IStatus> result = new AtomicReference<IStatus>();
+ try {
+ run(true, true, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ result.set(operation.run(monitor));
+ }
+ });
+ } catch (InvocationTargetException e) {
+ StatusManager.getManager().handle(
+ new Status(IStatus.ERROR, GerritUiPlugin.PLUGIN_ID,
+ "Unexpected error during execution of Gerrit operation", e),
+ StatusManager.SHOW | StatusManager.LOG);
+ } catch (InterruptedException e) {
+ // cancelled
+ return false;
+ }
+
+ if (result.get().getSeverity() == IStatus.CANCEL) {
+ return false;
+ }
+
+ if (!result.get().isOK()) {
+ StatusManager.getManager().handle(result.get(), StatusManager.SHOW | StatusManager.LOG);
+ return false;
+ }
+ return processOperationResult(operation);
+ }
+
+ protected boolean processOperationResult(GerritOperation<?> operation) {
+ return true;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ toolkit = new FormToolkit(TasksUiPlugin.getDefault().getFormColors(parent.getDisplay()));
+ Control control = super.createDialogArea(parent);
+ if (needsConfig()) {
+ GerritConfig config = getOperationFactory().getClient(getTask()).getGerritConfig();
+ if (config != null) {
+ doRefresh(config);
+ } else {
+ GerritOperation<GerritConfiguration> operation = getOperationFactory().createRefreshConfigOperation(
+ getTask(), new RefreshConfigRequest());
+ performOperation(operation);
+ config = operation.getOperationResult().getGerritConfig();
+ doRefresh(config);
+ }
+ }
+ return control;
+ }
+
+ protected RichTextEditor createRichTextEditor(Composite composite, String value) {
+ int style = SWT.FLAT | SWT.BORDER | SWT.MULTI | SWT.WRAP;
+
+ TaskRepository repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
+ task.getRepositoryUrl());
+ AbstractTaskEditorExtension extension = TaskEditorExtensions.getTaskEditorExtension(repository);
+
+ final RichTextEditor editor = new RichTextEditor(repository, style, null, extension, task);
+ editor.setText(value);
+ editor.createControl(composite, toolkit);
+
+ // HACK: this is to make sure that we can't have multiple things highlighted
+ editor.getViewer().getTextWidget().addFocusListener(new FocusListener() {
+
+ public void focusGained(FocusEvent e) {
+ }
+
+ public void focusLost(FocusEvent e) {
+ editor.getViewer().getTextWidget().setSelection(0);
+ }
+ });
+
+ return editor;
+ }
+
+ protected Text createPersonTextEditor(Composite composite, String value) {
+ int style = SWT.FLAT | SWT.BORDER | SWT.MULTI | SWT.WRAP;
+ Text editor = new Text(composite, style);
+ if (value != null) {
+ editor.setText(value);
+ }
+ TaskRepository repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
+ task.getRepositoryUrl());
+ ITasksUiFactory uiFactory = TasksUi.getUiFactory();
+ IContentProposalProvider proposalProvider = uiFactory.createPersonContentProposalProvider(repository);
+ ILabelProvider proposalLabelProvider = uiFactory.createPersonContentProposalLabelProvider(repository);
+
+ ContentAssistCommandAdapter adapter = new ContentAssistCommandAdapter(editor, new TextContentAdapter(),
+ proposalProvider, ContentAssistCommandAdapter.CONTENT_PROPOSAL_COMMAND, new char[0], true);
+ adapter.setLabelProvider(proposalLabelProvider);
+ adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
+
+ return editor;
+ }
+
+ protected void doRefresh(GerritConfig config) {
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/PublishDialog.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/PublishDialog.java
new file mode 100644
index 000000000..6a5c8e4cf
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/PublishDialog.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.operations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.mylyn.internal.gerrit.core.client.compat.PatchSetPublishDetailX;
+import org.eclipse.mylyn.internal.gerrit.core.client.compat.PermissionLabel;
+import org.eclipse.mylyn.internal.gerrit.core.operations.GerritOperation;
+import org.eclipse.mylyn.internal.gerrit.core.operations.PublishRequest;
+import org.eclipse.mylyn.internal.tasks.ui.editors.RichTextEditor;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.GerritConfig;
+import com.google.gerrit.common.data.PatchSetPublishDetail;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+
+/**
+ * @author Steffen Pingel
+ */
+public class PublishDialog extends GerritOperationDialog {
+
+ private static final String KEY_ID = "ApprovalCategoryValue.Id"; //$NON-NLS-1$
+
+ private final PatchSetPublishDetail publishDetail;
+
+ private RichTextEditor messageEditor;
+
+ private Composite approvalComposite;
+
+ private final List<Button> approvalButtons;
+
+ private Label statusLabel;
+
+ private final int addedDrafts;
+
+ public PublishDialog(Shell parentShell, ITask task, PatchSetPublishDetail publishDetail, int addedDrafts) {
+ super(parentShell, task);
+ this.publishDetail = publishDetail;
+ this.addedDrafts = addedDrafts;
+ this.approvalButtons = new ArrayList<Button>();
+ setNeedsConfig(true);
+ }
+
+ @Override
+ public GerritOperation<Object> createOperation() {
+ int patchSetId = publishDetail.getPatchSetInfo().getKey().get();
+ PublishRequest request = new PublishRequest(task.getTaskId(), patchSetId, getApprovals());
+ request.setMessage(messageEditor.getText());
+ return getOperationFactory().createPublishOperation(task, request);
+ }
+
+ private Set<ApprovalCategoryValue.Id> getApprovals() {
+ Set<ApprovalCategoryValue.Id> approvals = new HashSet<ApprovalCategoryValue.Id>();
+ for (Button button : approvalButtons) {
+ if (button.getSelection()) {
+ approvals.add((ApprovalCategoryValue.Id) button.getData(KEY_ID));
+ }
+ }
+ return approvals;
+ }
+
+ @Override
+ protected Control createPageControls(Composite parent) {
+ String changeId = publishDetail.getChange().getKey().abbreviate();
+
+ setTitle("Publish Comments");
+ setMessage(NLS.bind("Change {0} - {1}", changeId, publishDetail.getPatchSetInfo().getSubject()));
+
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(1, false));
+
+ approvalComposite = new Composite(composite, SWT.NONE);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(approvalComposite);
+ GridLayout layout = new GridLayout(1, false);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ approvalComposite.setLayout(layout);
+
+ messageEditor = createRichTextEditor(composite, "");
+ GridDataFactory.fillDefaults().grab(true, true).minSize(400, 150).applyTo(messageEditor.getControl());
+ messageEditor.getControl().setFocus();
+
+ statusLabel = new Label(composite, SWT.NONE);
+ statusLabel.setText(NLS.bind("Publishes {0} draft(s).", publishDetail.getDrafts().size() + addedDrafts));
+
+ return composite;
+ }
+
+ @Override
+ protected void doRefresh(GerritConfig config) {
+ Control[] children = approvalComposite.getChildren();
+ for (Control child : children) {
+ child.dispose();
+ }
+ approvalButtons.clear();
+
+ if (config == null) {
+ return;
+ }
+
+ if (config.getApprovalTypes() != null && config.getApprovalTypes().getApprovalTypes() != null) {
+ for (ApprovalType approvalType : config.getApprovalTypes().getApprovalTypes()) {
+ Set<ApprovalCategoryValue.Id> allowed = getAllowed(publishDetail, approvalType);
+ if (allowed != null && allowed.size() > 0) {
+ Group group = new Group(approvalComposite, SWT.NONE);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+ group.setText(approvalType.getCategory().getName());
+ group.setLayout(new RowLayout(SWT.VERTICAL));
+
+ int givenValue = 0;
+ if (publishDetail.getGiven() != null) {
+ // Gerrit 2.1
+ PatchSetApproval approval = publishDetail.getGiven().get(approvalType.getCategory().getId());
+ if (approval != null) {
+ givenValue = approval.getValue();
+ }
+ }
+
+ List<ApprovalCategoryValue.Id> allowedList = new ArrayList<ApprovalCategoryValue.Id>(allowed);
+ Collections.sort(allowedList, new Comparator<ApprovalCategoryValue.Id>() {
+ public int compare(ApprovalCategoryValue.Id o1, ApprovalCategoryValue.Id o2) {
+ return o2.get() - o1.get();
+ }
+ });
+ for (ApprovalCategoryValue.Id valueId : allowedList) {
+ ApprovalCategoryValue approvalValue = approvalType.getValue(valueId.get());
+
+ Button button = new Button(group, SWT.RADIO);
+ button.setText(approvalValue.format());
+ if (approvalValue.getValue() == givenValue) {
+ button.setSelection(true);
+ }
+
+ button.setData(KEY_ID, valueId);
+ approvalButtons.add(button);
+ }
+ }
+ }
+ }
+ }
+
+ private Set<ApprovalCategoryValue.Id> getAllowed(PatchSetPublishDetail publishDetail, ApprovalType approvalType) {
+ if (publishDetail.getAllowed() != null) {
+ // Gerrit 2.1
+ return publishDetail.getAllowed(approvalType.getCategory().getId());
+ } else if (publishDetail instanceof PatchSetPublishDetailX) {
+ // Gerrit 2.2
+ List<PermissionLabel> labels = ((PatchSetPublishDetailX) publishDetail).getLabels();
+ if (labels != null) {
+ Set<ApprovalCategoryValue.Id> result = new HashSet<ApprovalCategoryValue.Id>();
+ for (PermissionLabel label : labels) {
+ if (label.matches(approvalType.getCategory())) {
+ for (ApprovalCategoryValue value : approvalType.getValues()) {
+ if (label.matches(value)) {
+ result.add(value.getId());
+ }
+ }
+ }
+ }
+ return result;
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/RestoreDialog.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/RestoreDialog.java
new file mode 100644
index 000000000..a0b9f99cd
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/RestoreDialog.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.operations;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.mylyn.internal.gerrit.core.operations.GerritOperation;
+import org.eclipse.mylyn.internal.gerrit.core.operations.RestoreRequest;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.internal.tasks.ui.editors.RichTextEditor;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.reviewdb.PatchSet;
+
+/**
+ * @author Steffen Pingel
+ */
+public class RestoreDialog extends GerritOperationDialog {
+
+ private final PatchSet patchSet;
+
+ private RichTextEditor messageEditor;
+
+ public RestoreDialog(Shell parentShell, ITask task, PatchSet patchSet) {
+ super(parentShell, task);
+ this.patchSet = patchSet;
+ }
+
+ @Override
+ public GerritOperation<ChangeDetail> createOperation() {
+ int patchSetId = patchSet.getId().get();
+ RestoreRequest request = new RestoreRequest(task.getTaskId(), patchSetId);
+ request.setMessage(messageEditor.getText());
+ return GerritUiPlugin.getDefault().getOperationFactory().createRestoreOperation(task, request);
+ }
+
+ @Override
+ protected Control createPageControls(Composite parent) {
+ setTitle("Restore Change");
+ setMessage("Enter an optional message.");
+
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(1, false));
+
+ messageEditor = createRichTextEditor(composite, "");
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(messageEditor.getControl());
+
+ return composite;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/SubmitDialog.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/SubmitDialog.java
new file mode 100644
index 000000000..a13b9eb5d
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/operations/SubmitDialog.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Tasktop Technologies and others.
+ * 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:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.operations;
+
+import org.eclipse.mylyn.internal.gerrit.core.operations.GerritOperation;
+import org.eclipse.mylyn.internal.gerrit.core.operations.SubmitRequest;
+import org.eclipse.mylyn.internal.gerrit.ui.GerritUiPlugin;
+import org.eclipse.mylyn.internal.tasks.ui.editors.RichTextEditor;
+import org.eclipse.mylyn.tasks.core.ITask;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.reviewdb.PatchSet;
+
+/**
+ * @author Steffen Pingel
+ */
+public class SubmitDialog extends GerritOperationDialog {
+
+ private final PatchSet patchSet;
+
+ private RichTextEditor messageEditor;
+
+ public SubmitDialog(Shell parentShell, ITask task, PatchSet patchSet) {
+ super(parentShell, task);
+ this.patchSet = patchSet;
+ }
+
+ @Override
+ public GerritOperation<ChangeDetail> createOperation() {
+ int patchSetId = patchSet.getId().get();
+ SubmitRequest request = new SubmitRequest(task.getTaskId(), patchSetId);
+ return GerritUiPlugin.getDefault().getOperationFactory().createSubmitOperation(task, request);
+ }
+
+ @Override
+ protected Control createPageControls(Composite parent) {
+ setTitle("Submit Change");
+ setMessage("");
+
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(1, false));
+
+ Label label = new Label(composite, SWT.NONE);
+ label.setText("Submit Change?");
+
+ return composite;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/GerritCustomQueryPage.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/GerritCustomQueryPage.java
new file mode 100644
index 000000000..1245386f2
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/GerritCustomQueryPage.java
@@ -0,0 +1,230 @@
+/*********************************************************************
+ * Copyright (c) 2010 Sony Ericsson/ST Ericsson and others.
+ * 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:
+ * Sony Ericsson/ST Ericsson - initial API and implementation
+ * Sascha Scholz (SAP) - improvements
+ * Tasktop Technologies - improvements
+ *********************************************************************/
+package org.eclipse.mylyn.internal.gerrit.ui.wizards;
+
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.jface.fieldassist.TextContentAdapter;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.mylyn.commons.workbench.forms.SectionComposite;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.core.GerritCorePlugin;
+import org.eclipse.mylyn.internal.gerrit.core.GerritQuery;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient;
+import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.ui.wizards.AbstractRepositoryQueryPage2;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
+
+/**
+ * @author Mikael Kober
+ * @author Thomas Westling
+ * @author Sascha Scholz
+ */
+public class GerritCustomQueryPage extends AbstractRepositoryQueryPage2 {
+
+ private Button myChangesButton;
+
+ private Button watchedChangesButton;
+
+ private Button allOpenChangesButton;
+
+ private Button byProjectButton;
+
+ private Button customQueryButton;
+
+ private Text projectText;
+
+ private Text queryText;
+
+ public GerritCustomQueryPage(TaskRepository repository, String pageName, IRepositoryQuery query) {
+ super(pageName, repository, query);
+ setDescription("Enter title and select a query type.");
+ setNeedsClear(true);
+ setNeedsRefresh(true);
+ }
+
+ @Override
+ protected void createPageContent(SectionComposite parent) {
+ Composite composite = parent.getContent();
+ composite.setLayout(new GridLayout(2, false));
+
+ ModifyListener modifyListener = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ updateButtons();
+ }
+ };
+
+ Group group = new Group(composite, SWT.NONE);
+ group.setText("Query type");
+ GridLayoutFactory.swtDefaults().numColumns(2).spacing(7, 5).applyTo(group);
+ GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(group);
+
+ // radio button to select query type
+ myChangesButton = new Button(group, SWT.RADIO);
+ myChangesButton.setText("My changes");
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(myChangesButton);
+
+ watchedChangesButton = new Button(group, SWT.RADIO);
+ watchedChangesButton.setText("My watched changes");
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(watchedChangesButton);
+
+ allOpenChangesButton = new Button(group, SWT.RADIO);
+ allOpenChangesButton.setText("All open changes");
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(allOpenChangesButton);
+
+ byProjectButton = new Button(group, SWT.RADIO);
+ byProjectButton.setText("Open changes by project:");
+
+ projectText = new Text(group, SWT.BORDER);
+ projectText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
+ projectText.addModifyListener(modifyListener);
+ addProjectNameContentProposal(projectText);
+
+ new Label(composite, SWT.NONE);
+ customQueryButton = new Button(group, SWT.RADIO);
+ customQueryButton.setText("Custom query:");
+
+ queryText = new Text(group, SWT.BORDER);
+ queryText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
+ queryText.addModifyListener(modifyListener);
+
+ SelectionListener buttonSelectionListener = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ updateButtons();
+ }
+ };
+ byProjectButton.addSelectionListener(buttonSelectionListener);
+ customQueryButton.addSelectionListener(buttonSelectionListener);
+ }
+
+ private void addProjectNameContentProposal(Text text) {
+ IContentProposalProvider proposalProvider = new ProjectNameContentProposalProvider(
+ GerritCorePlugin.getDefault().getConnector(), getTaskRepository());
+ ContentAssistCommandAdapter adapter = new ContentAssistCommandAdapter(text, new TextContentAdapter(),
+ proposalProvider, null, new char[0], true);
+ adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
+ }
+
+ private GerritClient getGerritClient() {
+ GerritConnector connector = GerritCorePlugin.getDefault().getConnector();
+ return connector.getClient(getTaskRepository());
+ }
+
+ protected void updateButtons() {
+ projectText.setEnabled(byProjectButton.getSelection());
+ queryText.setEnabled(customQueryButton.getSelection());
+
+ IWizardContainer c = getContainer();
+ if (c != null && c.getCurrentPage() != null) {
+ c.updateButtons();
+ }
+ }
+
+ @Override
+ public boolean isPageComplete() {
+ boolean ret = (getQueryTitle() != null && getQueryTitle().trim().length() > 0);
+ if (byProjectButton != null && byProjectButton.getSelection()) {
+ ret &= (projectText != null && projectText.getText().length() > 0);
+ }
+ if (customQueryButton != null && customQueryButton.getSelection()) {
+ ret &= (queryText != null && queryText.getText().length() > 0);
+ }
+ return ret;
+ }
+
+ @Override
+ public void applyTo(IRepositoryQuery query) {
+ query.setSummary(getQueryTitle());
+ if (myChangesButton.getSelection()) {
+ query.setAttribute(GerritQuery.TYPE, GerritQuery.MY_CHANGES);
+ } else if (watchedChangesButton.getSelection()) {
+ query.setAttribute(GerritQuery.TYPE, GerritQuery.MY_WATCHED_CHANGES);
+ } else if (byProjectButton.getSelection()) {
+ query.setAttribute(GerritQuery.TYPE, GerritQuery.OPEN_CHANGES_BY_PROJECT);
+ } else if (customQueryButton.getSelection()) {
+ query.setAttribute(GerritQuery.TYPE, GerritQuery.CUSTOM);
+ } else {
+ query.setAttribute(GerritQuery.TYPE, GerritQuery.ALL_OPEN_CHANGES);
+ }
+ query.setAttribute(GerritQuery.PROJECT, projectText.getText());
+ query.setAttribute(GerritQuery.QUERY_STRING, queryText.getText());
+ }
+
+ @Override
+ protected void doRefreshControls() {
+ // nothing to do, only the content assist uses the configuration
+ }
+
+ @Override
+ protected boolean hasRepositoryConfiguration() {
+ return getGerritClient().getConfiguration() != null;
+ }
+
+ @Override
+ protected void doClearControls() {
+ restoreState(null);
+ }
+
+ @Override
+ protected boolean restoreState(IRepositoryQuery query) {
+ if (query != null) {
+ setQueryTitle(query.getSummary());
+ if (GerritQuery.MY_CHANGES.equals(query.getAttribute(GerritQuery.TYPE))) {
+ myChangesButton.setSelection(true);
+ } else if (GerritQuery.MY_WATCHED_CHANGES.equals(query.getAttribute(GerritQuery.TYPE))) {
+ watchedChangesButton.setSelection(true);
+ } else if (GerritQuery.OPEN_CHANGES_BY_PROJECT.equals(query.getAttribute(GerritQuery.TYPE))) {
+ byProjectButton.setSelection(true);
+ } else if (GerritQuery.CUSTOM.equals(query.getAttribute(GerritQuery.TYPE))) {
+ customQueryButton.setSelection(true);
+ } else {
+ allOpenChangesButton.setSelection(true);
+ }
+ if (query.getAttribute(GerritQuery.PROJECT) != null) {
+ projectText.setText(query.getAttribute(GerritQuery.PROJECT));
+ }
+ if (query.getAttribute(GerritQuery.QUERY_STRING) != null) {
+ queryText.setText(query.getAttribute(GerritQuery.QUERY_STRING));
+ }
+ } else {
+ myChangesButton.setSelection(true);
+ watchedChangesButton.setSelection(false);
+ byProjectButton.setSelection(false);
+ customQueryButton.setSelection(false);
+ allOpenChangesButton.setSelection(false);
+ projectText.setText(""); //$NON-NLS-1$
+ queryText.setText(""); //$NON-NLS-1$
+ }
+ updateButtons();
+ return true;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/ProjectNameContentProposal.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/ProjectNameContentProposal.java
new file mode 100644
index 000000000..3d3f68a8d
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/ProjectNameContentProposal.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP and others.
+ * 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:
+ * Sascha Scholz (SAP) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.wizards;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+
+/**
+ * @author Sascha Scholz
+ */
+public class ProjectNameContentProposal implements IContentProposal {
+
+ private final String projectName;
+
+ public ProjectNameContentProposal(String projectName) {
+ this.projectName = projectName;
+ }
+
+ @Override
+ public String getContent() {
+ return projectName;
+ }
+
+ @Override
+ public int getCursorPosition() {
+ return projectName.length();
+ }
+
+ @Override
+ public String getLabel() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return null;
+ }
+
+}
diff --git a/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/ProjectNameContentProposalProvider.java b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/ProjectNameContentProposalProvider.java
new file mode 100644
index 000000000..b45a36b58
--- /dev/null
+++ b/org.eclipse.mylyn.gerrit.ui/src/org/eclipse/mylyn/internal/gerrit/ui/wizards/ProjectNameContentProposalProvider.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP and others.
+ * 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:
+ * Sascha Scholz (SAP) - initial API and implementation
+ * Tasktop Technologies - improvements
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.gerrit.ui.wizards;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
+import org.eclipse.mylyn.internal.gerrit.core.GerritUtil;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient;
+import org.eclipse.mylyn.internal.gerrit.core.client.GerritConfiguration;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+
+import com.google.gerrit.reviewdb.Project;
+
+/**
+ * @author Sascha Scholz
+ * @author Steffen Pingel
+ */
+public class ProjectNameContentProposalProvider implements IContentProposalProvider {
+
+ private static class MissingConfigurationContentProposal implements IContentProposal {
+
+ @Override
+ public String getContent() {
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public int getCursorPosition() {
+ return 0;
+ }
+
+ @Override
+ public String getLabel() {
+ return "(Repository configuration needs to be refreshed)";
+ }
+
+ @Override
+ public String getDescription() {
+ // ignore
+ return null;
+ }
+
+ }
+
+ private final TaskRepository repository;
+
+ private final GerritConnector connector;
+
+ public ProjectNameContentProposalProvider(GerritConnector connector, TaskRepository repository) {
+ this.connector = connector;
+ this.repository = repository;
+ }
+
+ @Override
+ public IContentProposal[] getProposals(String contents, int position) {
+ String contentsLowerCase = contents.toLowerCase(Locale.ENGLISH);
+ ArrayList<IContentProposal> proposals = new ArrayList<IContentProposal>();
+ GerritClient client = connector.getClient(repository);
+ List<Project> projects = getProjects(client);
+ if (projects != null) {
+ for (Project project : projects) {
+ String projectName = project.getName();
+ if (projectName.toLowerCase(Locale.ENGLISH).contains(contentsLowerCase)
+ && !GerritUtil.isPermissionOnlyProject(client, projectName)) {
+ proposals.add(new ProjectNameContentProposal(projectName));
+ }
+ }
+ return proposals.toArray(new IContentProposal[] {});
+ } else {
+ // TODO Trigger refresh of repository configuration
+ return new IContentProposal[] { new MissingConfigurationContentProposal() };
+ }
+ }
+
+ private List<Project> getProjects(GerritClient client) {
+ GerritConfiguration config = client.getConfiguration();
+ if (config != null) {
+ return config.getProjects();
+ }
+ return null;
+ }
+
+}

Back to the top