/******************************************************************************* | |
* Copyright (c) 2015-2016 Ericsson | |
* 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: | |
* Guy Perron - initial API and implementation | |
* Jacques Bouthillier - Fill the message tab | |
* Jacques Bouthillier - Refactor the class | |
* Patrick-Jeffrey Pollo Guilbert - Add hover message on subject | |
*******************************************************************************/ | |
package org.eclipse.egerrit.internal.ui.editors; | |
import java.lang.reflect.InvocationTargetException; | |
import java.util.Iterator; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import java.util.concurrent.CompletableFuture; | |
import org.eclipse.core.databinding.DataBindingContext; | |
import org.eclipse.core.databinding.UpdateValueStrategy; | |
import org.eclipse.core.databinding.beans.BeanProperties; | |
import org.eclipse.core.databinding.conversion.NumberToStringConverter; | |
import org.eclipse.core.databinding.observable.value.IObservableValue; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.core.runtime.jobs.Job; | |
import org.eclipse.egerrit.internal.core.EGerritCorePlugin; | |
import org.eclipse.egerrit.internal.core.GerritClient; | |
import org.eclipse.egerrit.internal.core.command.AbandonCommand; | |
import org.eclipse.egerrit.internal.core.command.DeleteDraftChangeCommand; | |
import org.eclipse.egerrit.internal.core.command.PublishDraftChangeCommand; | |
import org.eclipse.egerrit.internal.core.command.RestoreCommand; | |
import org.eclipse.egerrit.internal.core.command.RevertCommand; | |
import org.eclipse.egerrit.internal.core.exception.EGerritException; | |
import org.eclipse.egerrit.internal.core.rest.AbandonInput; | |
import org.eclipse.egerrit.internal.core.rest.RestoreInput; | |
import org.eclipse.egerrit.internal.core.rest.RevertInput; | |
import org.eclipse.egerrit.internal.model.ActionConstants; | |
import org.eclipse.egerrit.internal.model.ChangeInfo; | |
import org.eclipse.egerrit.internal.model.ModelPackage; | |
import org.eclipse.egerrit.internal.model.RevisionInfo; | |
import org.eclipse.egerrit.internal.process.CherryPickProcess; | |
import org.eclipse.egerrit.internal.process.RebaseProcess; | |
import org.eclipse.egerrit.internal.process.ReplyProcess; | |
import org.eclipse.egerrit.internal.process.SubmitProcess; | |
import org.eclipse.egerrit.internal.ui.editors.model.ChangeDetailEditorInput; | |
import org.eclipse.egerrit.internal.ui.table.provider.DeleteDraftRevisionProvider; | |
import org.eclipse.egerrit.internal.ui.table.provider.PatchSetHandlerProvider; | |
import org.eclipse.egerrit.internal.ui.tabs.DetailsTabView; | |
import org.eclipse.egerrit.internal.ui.tabs.HistoryTabView; | |
import org.eclipse.egerrit.internal.ui.tabs.MessageTabView; | |
import org.eclipse.egerrit.internal.ui.tabs.ObservableCollector; | |
import org.eclipse.egerrit.internal.ui.utils.ActiveWorkspaceRevision; | |
import org.eclipse.egerrit.internal.ui.utils.Messages; | |
import org.eclipse.egerrit.internal.ui.utils.UIUtils; | |
import org.eclipse.emf.common.notify.Notification; | |
import org.eclipse.emf.databinding.EMFProperties; | |
import org.eclipse.emf.ecore.util.EContentAdapter; | |
import org.eclipse.jface.action.MenuManager; | |
import org.eclipse.jface.action.Separator; | |
import org.eclipse.jface.databinding.swt.WidgetProperties; | |
import org.eclipse.jface.dialogs.IInputValidator; | |
import org.eclipse.jface.dialogs.InputDialog; | |
import org.eclipse.jface.dialogs.MessageDialog; | |
import org.eclipse.jface.window.Window; | |
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.events.SelectionListener; | |
import org.eclipse.swt.graphics.Point; | |
import org.eclipse.swt.graphics.Rectangle; | |
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.Control; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Event; | |
import org.eclipse.swt.widgets.Group; | |
import org.eclipse.swt.widgets.Label; | |
import org.eclipse.swt.widgets.Listener; | |
import org.eclipse.swt.widgets.MenuItem; | |
import org.eclipse.swt.widgets.TabFolder; | |
import org.eclipse.swt.widgets.Text; | |
import org.eclipse.swt.widgets.ToolTip; | |
import org.eclipse.ui.IEditorInput; | |
import org.eclipse.ui.IEditorPart; | |
import org.eclipse.ui.IEditorSite; | |
import org.eclipse.ui.IWorkbench; | |
import org.eclipse.ui.IWorkbenchPage; | |
import org.eclipse.ui.PartInitException; | |
import org.eclipse.ui.PlatformUI; | |
import org.eclipse.ui.part.EditorPart; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import com.ibm.icu.text.NumberFormat; | |
public class ChangeDetailEditor extends EditorPart { | |
private static Logger logger = LoggerFactory.getLogger(ChangeDetailEditor.class); | |
/** | |
* The ID of the view as specified by the extension. | |
*/ | |
public static final String EDITOR_ID = "org.eclipse.egerrit.ui.editors.ChangeDetailEditor"; //$NON-NLS-1$ | |
public static int REFRESH_RATE = 60000; | |
private DetailsTabView detailsTab = null; | |
private HistoryTabView historytab = null; | |
private MessageTabView messageTab = null; | |
private ChangeInfo fChangeInfo; | |
private GerritClient fGerritClient = null; | |
private Text subjectData; | |
private Text statusData; | |
private Text shortIdData; | |
private Composite headerSection; | |
private ModelLoader loader; | |
// ------------------------------------------------------------------------ | |
// Constructor and life cycle | |
// ------------------------------------------------------------------------ | |
private EContentAdapter subjectListener; | |
private DataBindingContext bindingContext = new DataBindingContext(); | |
private DeleteDraftRevisionProvider deleteDraftRevision = null; | |
private PatchSetHandlerProvider patchSetSelector; | |
private HideControlObservable hidableRevertButton; | |
private HideControlObservable hidableSubmitButton; | |
private ObservableCollector observableCollector; | |
private CheckForUpdate checkForUpdates; | |
/** | |
* The constructor. | |
*/ | |
public ChangeDetailEditor() { | |
super(); | |
} | |
@Override | |
public void createPartControl(final Composite parent) { | |
loader = ModelLoader.initialize(fGerritClient, fChangeInfo); | |
loader.loadBasicInformation(false); | |
parent.setLayout(new GridLayout(1, false)); | |
parent.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); | |
headerSection = headerSection(parent); | |
headerSection.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); | |
TabFolder tabFolder = new TabFolder(parent, SWT.NONE); | |
tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | |
tabFolder.setBackground(parent.getBackground()); | |
historytab = new HistoryTabView(); | |
historytab.create(fGerritClient, tabFolder, fChangeInfo); | |
messageTab = new MessageTabView(); | |
messageTab.create(fGerritClient, tabFolder, fChangeInfo); | |
detailsTab = new DetailsTabView(); | |
detailsTab.create(fGerritClient, tabFolder, fChangeInfo); | |
Composite compButton = buttonSection(parent); | |
compButton.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false)); | |
setPartName(((ChangeDetailEditorInput) getEditorInput()).getName()); | |
observableCollector = new ObservableCollector(bindingContext); | |
checkForUpdates = new CheckForUpdate(); | |
checkForUpdates.schedule(REFRESH_RATE); | |
} | |
private Composite headerSection(final Composite parent) { | |
Group group_header = new Group(parent, SWT.NONE); | |
group_header.setLayout(new GridLayout(10, false)); | |
group_header.setBackground(parent.getBackground()); | |
Label lblId = new Label(group_header, SWT.NONE); | |
lblId.setText(Messages.ChangeDetailEditor_2); | |
shortIdData = new Text(group_header, SWT.NONE); | |
shortIdData.setEditable(false); | |
shortIdData.setBackground(parent.getBackground()); | |
shortIdData.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1)); | |
statusData = new Text(group_header, SWT.LEFT); | |
GridData gd_lblStatus = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); | |
statusData.setLayoutData(gd_lblStatus); | |
statusData.setEditable(false); | |
statusData.addListener(SWT.Modify, new Listener() { | |
@Override | |
public void handleEvent(Event event) { | |
statusData.getParent().layout(); | |
} | |
}); | |
Label lblSubject = new Label(group_header, SWT.NONE); | |
lblSubject.setText(Messages.ChangeDetailEditor_3); | |
GridData gd_Subject = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); | |
gd_Subject.horizontalIndent = 10; | |
lblSubject.setLayoutData(gd_Subject); | |
subjectData = new Text(group_header, SWT.NONE); | |
GridData gd_lblSubjectData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1); | |
subjectData.setLayoutData(gd_lblSubjectData); | |
subjectData.setEditable(false); | |
subjectData.setBackground(parent.getBackground()); | |
final ToolTip tip = new ToolTip(parent.getShell(), SWT.NONE); | |
subjectData.addListener(SWT.MouseMove, new Listener() { | |
@Override | |
public void handleEvent(Event event) { | |
tip.setVisible(false); | |
} | |
}); | |
subjectData.addListener(SWT.MouseHover, new Listener() { | |
@Override | |
public void handleEvent(Event event) { | |
tip.setMessage(subjectData.getText()); | |
tip.setVisible(true); | |
} | |
}); | |
subjectData.addListener(SWT.Modify, new Listener() { | |
@Override | |
public void handleEvent(Event event) { | |
subjectData.getParent().layout(); | |
} | |
}); | |
final String ACTIVATION_MESSAGE = Messages.ChangeDetailEditor_4; | |
Button activeReview = new Button(group_header, SWT.CHECK); | |
activeReview.setSelection(false); | |
activeReview.setText(ACTIVATION_MESSAGE); | |
activeReview.setToolTipText(Messages.ChangeDetailEditor_5); | |
IObservableValue observeActiveRevisionStateForButtonText = BeanProperties.value("activeRevision") //$NON-NLS-1$ | |
.observe(ActiveWorkspaceRevision.getInstance()); | |
bindingContext.bindValue(WidgetProperties.text().observe(activeReview), observeActiveRevisionStateForButtonText, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new UpdateValueStrategy() { | |
@Override | |
public Object convert(Object value) { | |
if (value == null) { | |
return ACTIVATION_MESSAGE; | |
} | |
if (((RevisionInfo) value).getChangeInfo().getId().equals(fChangeInfo.getId())) { | |
return Messages.ChangeDetailEditor_7 + (((RevisionInfo) value)).get_number(); | |
} | |
return ACTIVATION_MESSAGE; | |
} | |
}); | |
IObservableValue observeActiveRevisionStateForButtonEnablement = BeanProperties.value("activeRevision") //$NON-NLS-1$ | |
.observe(ActiveWorkspaceRevision.getInstance()); | |
bindingContext.bindValue(WidgetProperties.selection().observe(activeReview), | |
observeActiveRevisionStateForButtonEnablement, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new UpdateValueStrategy() { | |
@Override | |
public Object convert(Object value) { | |
if (value == null) { | |
return false; | |
} | |
if (((RevisionInfo) value).getChangeInfo().getId().equals(fChangeInfo.getId())) { | |
return true; | |
} | |
return false; | |
} | |
}); | |
activeReview.addSelectionListener(new SelectionListener() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
if (activeReview.getSelection()) { | |
CompletableFuture.runAsync(() -> { | |
ActiveWorkspaceRevision.getInstance().activateCurrentRevision(fGerritClient, | |
fChangeInfo.getUserSelectedRevision() != null | |
? fChangeInfo.getUserSelectedRevision() | |
: fChangeInfo.getRevision()); | |
}); | |
} else { | |
ActiveWorkspaceRevision.getInstance().deactiveCurrentRevision(); | |
} | |
} | |
@Override | |
public void widgetDefaultSelected(SelectionEvent e) { | |
// ignore | |
} | |
}); | |
//Add Delete revision button for DRAFT patchset | |
deleteDraftRevision = new DeleteDraftRevisionProvider(); | |
deleteDraftRevision.create(group_header, fGerritClient, fChangeInfo); | |
//Create the PatchSetButton | |
patchSetSelector = new PatchSetHandlerProvider(); | |
patchSetSelector.create(group_header, fChangeInfo); | |
//Set the binding for this section | |
headerSectionDataBindings(); | |
return group_header; | |
} | |
private Composite buttonSection(final Composite parent) { | |
final int NUMBER_OF_BUTTONS = 8; | |
final Composite c = new Composite(parent, SWT.NONE); | |
c.setLayout(new GridLayout(NUMBER_OF_BUTTONS, true)); | |
Button refresh = new Button(c, SWT.PUSH); | |
refresh.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
refresh.setText(ActionConstants.REFRESH.getLiteral()); | |
refresh.setToolTipText(Messages.ChangeDetailEditor_9); | |
refresh.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
refreshStatus(); | |
} | |
}); | |
Button submitButton = new Button(c, SWT.PUSH); | |
submitButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
submitButton.setText(ActionConstants.SUBMIT.getLiteral()); | |
submitButton.setEnabled(false); | |
Button revertButton = new Button(c, SWT.PUSH); | |
revertButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
revertButton.setText(ActionConstants.REVERT.getLiteral()); | |
revertButton.setEnabled(false); | |
//Bind the submit button | |
{ | |
IObservableValue observeSubmitable = EMFProperties | |
.value(ModelPackage.Literals.CHANGE_INFO__USER_SELECTED_REVISION) | |
.value(ModelPackage.Literals.REVISION_INFO__SUBMITABLE) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.enabled().observe(submitButton), observeSubmitable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); | |
bindingContext.bindValue(WidgetProperties.visible().observe(submitButton), observeSubmitable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new UpdateValueStrategy() { | |
@Override | |
public Object convert(Object value) { | |
if (value == null) { | |
return false; | |
} | |
boolean state = ((Boolean) value).booleanValue(); | |
if (state) { | |
return true; | |
} | |
if (revertButton.isVisible()) { | |
return false; | |
} | |
return true; | |
} | |
}); | |
} | |
//Bind the revert button | |
{ | |
IObservableValue observeRevertable = EMFProperties.value(ModelPackage.Literals.CHANGE_INFO__REVERTABLE) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.enabled().observe(revertButton), observeRevertable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); | |
bindingContext.bindValue(WidgetProperties.visible().observe(revertButton), observeRevertable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); | |
//When the revert is not enable, we need to force the submit button to be visible | |
bindingContext.bindValue(WidgetProperties.visible().observe(submitButton), observeRevertable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new NegateBooleanConverter()); | |
//We need to force a redraw of the layout to remove the whitespace that would otherwise be left by the buttons being hidden | |
hidableSubmitButton = new HideControlObservable(submitButton); | |
bindingContext.bindValue(hidableSubmitButton, observeRevertable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new NegateBooleanConverter()); | |
hidableRevertButton = new HideControlObservable(revertButton); | |
bindingContext.bindValue(new HideControlObservable(revertButton), observeRevertable, null, null); | |
} | |
submitButton.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
super.widgetSelected(e); | |
SubmitProcess submitProcess = new SubmitProcess(); | |
submitProcess.handleSubmit(fChangeInfo, fGerritClient); | |
} | |
}); | |
revertButton.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
boolean revertSuccessfull = false; | |
String revertMsg = NLS.bind(Messages.Revert_message, new Object[] { fChangeInfo.getSubject(), | |
fChangeInfo.getRevision().getCommit().getCommit(), fChangeInfo.get_number() }); | |
ChangeInfo revertResult = null; | |
String revertErrorMessage = null; | |
while (!revertSuccessfull) { | |
final String errorMsg = revertErrorMessage; | |
final InputDialog replyDialog = new InputDialog(revertButton.getShell(), | |
Messages.Revert_dialog_title, Messages.Revert_dialog_message, revertMsg, | |
revertErrorMessage == null ? null : new IInputValidator() { | |
//Because InputDialog does not allow us to set the text w/o disabling the ok button, | |
//we need to trick the dialog in displaying what we want with this counter. | |
private int count = 0; | |
@Override | |
public String isValid(String newText) { | |
if (count == 0) { | |
count++; | |
return errorMsg; | |
} else { | |
return null; | |
} | |
} | |
}) { | |
@Override | |
protected int getInputTextStyle() { | |
return SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.WRAP; | |
} | |
@Override | |
protected Control createDialogArea(Composite parent) { | |
Control res = super.createDialogArea(parent); | |
((GridData) this.getText().getLayoutData()).heightHint = 100; | |
((GridData) this.getText().getLayoutData()).widthHint = 500; | |
return res; | |
} | |
}; | |
if (replyDialog.open() != Window.OK) { | |
return; | |
} | |
RevertCommand revertCmd = fGerritClient.revert(fChangeInfo.getId()); | |
RevertInput revertInput = new RevertInput(); | |
revertMsg = replyDialog.getValue(); | |
revertInput.setMessage(revertMsg); | |
revertCmd.setCommandInput(revertInput); | |
try { | |
revertResult = revertCmd.call(); | |
if (revertResult == null) { | |
revertErrorMessage = revertCmd.getFailureReason(); | |
} else { | |
revertSuccessfull = true; | |
} | |
} catch (EGerritException e3) { | |
EGerritCorePlugin | |
.logError(fGerritClient.getRepository().formatGerritVersion() + e3.getMessage()); | |
return; | |
} | |
} | |
UIUtils.openAnotherEditor(revertResult, fGerritClient); | |
} | |
}); | |
Button fAbandon = new Button(c, SWT.PUSH); | |
fAbandon.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
fAbandon.setEnabled(false); | |
fAbandon.setText(ActionConstants.ABANDON.getLiteral()); | |
Button fRestore = new Button(c, SWT.PUSH); | |
fRestore.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
fRestore.setEnabled(false); | |
fRestore.setText(ActionConstants.RESTORE.getLiteral()); | |
//Bind the abandon button | |
{ | |
IObservableValue observeAbandonable = EMFProperties.value(ModelPackage.Literals.CHANGE_INFO__ABANDONABLE) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.enabled().observe(fAbandon), observeAbandonable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); | |
bindingContext.bindValue(WidgetProperties.visible().observe(fAbandon), observeAbandonable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); | |
bindingContext.bindValue(WidgetProperties.visible().observe(fAbandon), observeAbandonable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new UpdateValueStrategy() { | |
@Override | |
public Object convert(Object value) { | |
boolean state = ((Boolean) value).booleanValue(); | |
if (state) { | |
return true; | |
} | |
if (fRestore.isVisible()) { | |
//We want to make sure to always show the submit button even if the restore is disabled so we at least get one button | |
return false; | |
} | |
return true; | |
} | |
}); | |
} | |
//Bind the restore button | |
{ | |
IObservableValue observeRestorable = EMFProperties.value(ModelPackage.Literals.CHANGE_INFO__RESTOREABLE) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.enabled().observe(fRestore), observeRestorable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); | |
bindingContext.bindValue(WidgetProperties.visible().observe(fRestore), observeRestorable, null, | |
new NegateBooleanConverter()); | |
//When restore is not enable, we need to force the abandon button to be visible | |
bindingContext.bindValue(WidgetProperties.visible().observe(fAbandon), observeRestorable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new NegateBooleanConverter()); | |
//We need to force a redraw of the layout to remove the whitespace that would otherwise be left by the buttons being hidden | |
bindingContext.bindValue(new HideControlObservable(fAbandon), observeRestorable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new NegateBooleanConverter()); | |
bindingContext.bindValue(new HideControlObservable(fRestore), observeRestorable, null, null); | |
} | |
fAbandon.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
super.widgetSelected(e); | |
InputDialog inputDialog = new InputDialog(fAbandon.getParent().getShell(), | |
Messages.ChangeDetailEditor_13, Messages.ChangeDetailEditor_14, "", null); //$NON-NLS-1$ | |
if (inputDialog.open() != Window.OK) { | |
return; | |
} | |
AbandonCommand abandonCmd = fGerritClient.abandon(fChangeInfo.getId()); | |
AbandonInput abandonInput = new AbandonInput(); | |
abandonInput.setMessage(inputDialog.getValue()); | |
abandonCmd.setCommandInput(abandonInput); | |
CompletableFuture.runAsync(() -> { | |
try { | |
abandonCmd.call(); | |
} catch (EGerritException e1) { | |
EGerritCorePlugin | |
.logError(fGerritClient.getRepository().formatGerritVersion() + e1.getMessage()); | |
} | |
refreshStatus(); | |
}).thenRun(() -> { | |
new RefreshRelatedEditors(fChangeInfo, fGerritClient).schedule(); | |
}); | |
} | |
}); | |
fRestore.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
super.widgetSelected(e); | |
InputDialog inputDialog = new InputDialog(fAbandon.getParent().getShell(), | |
Messages.ChangeDetailEditor_16, Messages.ChangeDetailEditor_17, "", null); //$NON-NLS-1$ | |
if (inputDialog.open() != Window.OK) { | |
return; | |
} | |
RestoreCommand restoreCmd = fGerritClient.restore(fChangeInfo.getId()); | |
RestoreInput restoreInput = new RestoreInput(); | |
restoreInput.setMessage(inputDialog.getValue()); | |
restoreCmd.setCommandInput(restoreInput); | |
CompletableFuture.runAsync(() -> { | |
try { | |
restoreCmd.call(); | |
} catch (EGerritException e1) { | |
EGerritCorePlugin | |
.logError(fGerritClient.getRepository().formatGerritVersion() + e1.getMessage()); | |
} | |
refreshStatus(); | |
}).thenRun(() -> { | |
new RefreshRelatedEditors(fChangeInfo, fGerritClient).schedule(); | |
}); | |
} | |
}); | |
Button rebaseButton = new Button(c, SWT.PUSH); | |
rebaseButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
rebaseButton.setText(ActionConstants.REBASE.getLiteral()); | |
//Bind the rebase button | |
{ | |
IObservableValue observeRebasable = EMFProperties | |
.value(ModelPackage.Literals.CHANGE_INFO__USER_SELECTED_REVISION) | |
.value(ModelPackage.Literals.REVISION_INFO__REBASEABLE) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.enabled().observe(rebaseButton), observeRebasable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); | |
} | |
rebaseButton.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
RebaseProcess rebaseProcess = new RebaseProcess(); | |
try { | |
rebaseProcess.handleRebase(rebaseButton.getShell(), fChangeInfo, | |
fChangeInfo.getUserSelectedRevision(), fGerritClient); | |
} catch (InvocationTargetException e1) { | |
EGerritCorePlugin.logError(e1.getMessage()); | |
} | |
} | |
}); | |
Button download = new Button(c, SWT.PUSH); | |
download.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
download.setText(Messages.ChangeDetailEditor_24); | |
download.addSelectionListener(downloadButtonListener(parent));//checkoutButtonListener(parent)); | |
Button cherryPickToRemoteBranch = new Button(c, SWT.PUSH); | |
cherryPickToRemoteBranch.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
cherryPickToRemoteBranch.setText(ActionConstants.CHERRYPICK.getLiteral()); | |
cherryPickToRemoteBranch.setToolTipText(Messages.ChangeDetailEditor_cherryPickBranch); | |
IObservableValue cherryPickAble = EMFProperties.value(ModelPackage.Literals.CHANGE_INFO__USER_SELECTED_REVISION) | |
.value(ModelPackage.Literals.REVISION_INFO__CHERRYPICKABLE) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.enabled().observe(cherryPickToRemoteBranch), cherryPickAble, null, | |
null); | |
cherryPickToRemoteBranch.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
CherryPickProcess cherryPickProcess = new CherryPickProcess(); | |
cherryPickProcess.handleCherryPick(cherryPickToRemoteBranch.getShell(), fGerritClient, fChangeInfo, | |
fChangeInfo.getUserSelectedRevision()); | |
} | |
}); | |
Button replyButton = new Button(c, SWT.PUSH | SWT.DROP_DOWN | SWT.ARROW_DOWN); | |
replyButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
replyButton.setText(ActionConstants.REPLY.getLiteral()); | |
if (fGerritClient.getRepository().getServerInfo().isAnonymous()) { | |
replyButton.setEnabled(false); | |
} | |
replyButton.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
//Adjust the list of label which the current user can set to a maximum value | |
String loginUser = fGerritClient.getRepository().getServerInfo().getUserName(); | |
Map<String, Integer> adjustedAllowedButton = fChangeInfo.getLabelsNotAtMax(loginUser); | |
if (adjustedAllowedButton.isEmpty()) { | |
//Should open directly the reply dialog, no sub-menu | |
ReplyProcess replyProcess = new ReplyProcess(); | |
replyProcess.handleReplyDialog(replyButton.getShell(), fChangeInfo, fGerritClient, | |
fChangeInfo.getUserSelectedRevision()); | |
} else { | |
MenuManager mgr = new MenuManager(); | |
buildReplyDynamicMenu(adjustedAllowedButton, mgr); | |
mgr.createContextMenu(replyButton).setVisible(true); | |
} | |
} | |
}); | |
Button draftButton = new Button(c, SWT.PUSH | SWT.DROP_DOWN | SWT.ARROW_DOWN); | |
draftButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); | |
draftButton.setText(ActionConstants.DRAFT.getLiteral()); | |
IObservableValue observeDeleteable = EMFProperties.value(ModelPackage.Literals.CHANGE_INFO__DELETEABLE) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.enabled().observe(draftButton), observeDeleteable, | |
new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), null); | |
draftButton.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
org.eclipse.swt.widgets.Menu menu = new org.eclipse.swt.widgets.Menu(draftButton.getShell(), | |
SWT.POP_UP); | |
final MenuItem itemPublish = new MenuItem(menu, SWT.PUSH); | |
itemPublish.setText(ActionConstants.PUBLISH.getLiteral()); | |
itemPublish.addSelectionListener(new SelectionListener() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
PublishDraftChangeCommand publishCommand = fGerritClient | |
.publishDraftChange(fChangeInfo.getId()); | |
try { | |
publishCommand.call(); | |
refreshStatus(); | |
} catch (EGerritException e1) { | |
EGerritCorePlugin | |
.logError(fGerritClient.getRepository().formatGerritVersion() + e1.getMessage()); | |
} | |
} | |
@Override | |
public void widgetDefaultSelected(SelectionEvent e) { | |
// ignore | |
} | |
}); | |
MenuItem itemDeleteDraftChange = new MenuItem(menu, SWT.PUSH); | |
itemDeleteDraftChange.setText(Messages.ChangeDetailEditor_27); | |
itemDeleteDraftChange.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
super.widgetSelected(e); | |
if (!MessageDialog.openConfirm(Display.getDefault().getActiveShell(), | |
Messages.ChangeDetailEditor_28, | |
NLS.bind(Messages.ChangeDetailEditor_29, fChangeInfo.getSubject()))) { | |
return; | |
} | |
DeleteDraftChangeCommand deleteDraftChangeCmd = fGerritClient | |
.deleteDraftChange(fChangeInfo.getId()); | |
try { | |
deleteDraftChangeCmd.call(); | |
IWorkbench workbench = PlatformUI.getWorkbench(); | |
final IWorkbenchPage activePage = workbench.getActiveWorkbenchWindow().getActivePage(); | |
IEditorPart editor = activePage.getActiveEditor(); | |
activePage.closeEditor(editor, false); | |
} catch (EGerritException e1) { | |
EGerritCorePlugin | |
.logError(fGerritClient.getRepository().formatGerritVersion() + e1.getMessage()); | |
} | |
} | |
}); | |
Point loc = draftButton.getLocation(); | |
Rectangle rect = draftButton.getBounds(); | |
Point mLoc = new Point(loc.x - 1, loc.y + rect.height); | |
menu.setLocation(draftButton.getDisplay().map(draftButton.getParent(), null, mLoc)); | |
menu.setVisible(true); | |
} | |
}); | |
c.setSize(c.computeSize(SWT.DEFAULT, SWT.DEFAULT)); | |
return c; | |
} | |
private void buildReplyDynamicMenu(Map<String, Integer> adjustedAllowedButton, MenuManager mgr) { | |
mgr.add(new ReplyHandler(fChangeInfo, fGerritClient, ActionConstants.REPLY.getLiteral())); //Add the Reply option | |
//Adjust the list of label which the current user can set to a maximum value | |
int size = adjustedAllowedButton.size(); | |
//If there is some menu to add, then lets create a separator | |
if (size > 0) { | |
mgr.add(new Separator()); | |
} | |
//Add all potential menu items | |
Iterator<Entry<String, Integer>> iter = adjustedAllowedButton.entrySet().iterator(); | |
while (iter.hasNext()) { | |
Entry<String, Integer> entry = iter.next(); | |
mgr.add(new ReplyHandler(fChangeInfo, fGerritClient, entry.getKey())); | |
} | |
//Add a selection for all buttons when there is more than one selection | |
if (size > 1) { | |
mgr.add(new ReplyHandler(fChangeInfo, fGerritClient, ReplyProcess.REPLY_ALL_BUTTONS)); | |
} | |
} | |
private SelectionListener downloadButtonListener(Composite parent) { | |
return new SelectionListener() { | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
MenuManager mgr = new MenuManager(); | |
mgr.add(new CherryPickRevision(fChangeInfo.getUserSelectedRevision(), fGerritClient)); | |
mgr.add(new CheckoutRevision(fChangeInfo.getUserSelectedRevision(), fGerritClient)); | |
mgr.createContextMenu(parent).setVisible(true); | |
} | |
@Override | |
public void widgetDefaultSelected(SelectionEvent e) { | |
//Nothing to do | |
} | |
}; | |
} | |
@Override | |
public void setFocus() { | |
headerSection.setFocus(); | |
} | |
/** | |
* This is the entry point to fill the editor with the Change info and the gerrit server being used. | |
* | |
* @param GerritClient | |
* gerritClient | |
* @param ChangeInfo | |
* element | |
*/ | |
private void setChangeInfo(GerritClient gerritClient, ChangeInfo element) { | |
fGerritClient = gerritClient; | |
if (fChangeInfo == null) { | |
fChangeInfo = element; | |
//Initial setting for the user selected revision | |
fChangeInfo.setUserSelectedRevision(element.getRevisions().get(element.getCurrent_revision())); | |
} else { | |
//Refresh only potential modified fields, otherwise the data binding seems not to know which fields are modified | |
fChangeInfo.set_number(element.get_number()); | |
fChangeInfo.setChange_id(element.getChange_id()); | |
fChangeInfo.setStatus(element.getStatus()); | |
fChangeInfo.setStarred(element.isStarred()); | |
fChangeInfo.setCreated(element.getCreated()); | |
fChangeInfo.setUpdated(element.getUpdated()); | |
fChangeInfo.setReviewed(element.isReviewed()); | |
fChangeInfo.setMergeable(element.isMergeable()); | |
fChangeInfo.setInsertions(element.getInsertions()); | |
fChangeInfo.setDeletions(element.getDeletions()); | |
fChangeInfo.setCurrent_revision(element.getCurrent_revision()); | |
} | |
} | |
/** | |
* Refreshes the ChangeEditor | |
*/ | |
public void refreshStatus() { | |
//Force change the updated timestamp to cause a full reload to happen | |
loader.reload(true); | |
} | |
protected void headerSectionDataBindings() { | |
//Show id | |
IObservableValue idObservable = EMFProperties.value(ModelPackage.Literals.CHANGE_INFO__NUMBER) | |
.observe(fChangeInfo); | |
NumberFormat numberFormat = NumberFormat.getInstance(); | |
numberFormat.setGroupingUsed(false); | |
bindingContext.bindValue(WidgetProperties.text().observe(shortIdData), idObservable, null, | |
new UpdateValueStrategy().setConverter(NumberToStringConverter.fromInteger(numberFormat, true))); | |
//Show subject | |
IObservableValue subjectObservable = EMFProperties.value(ModelPackage.Literals.CHANGE_INFO__SUBJECT) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.text().observe(subjectData), subjectObservable, null, null); | |
fChangeInfo.eAdapters().add(getSubjectListener()); | |
//Show status | |
IObservableValue statusObserveValue = EMFProperties.value(ModelPackage.Literals.CHANGE_INFO__STATUS) | |
.observe(fChangeInfo); | |
bindingContext.bindValue(WidgetProperties.text().observe(statusData), statusObserveValue, null, null); | |
} | |
private EContentAdapter getSubjectListener() { | |
subjectListener = new EContentAdapter() { | |
@Override | |
public void notifyChanged(Notification msg) { | |
if (msg.getFeature() == null) { | |
return; | |
} | |
if (msg.getFeature().equals(ModelPackage.Literals.CHANGE_INFO__SUBJECT)) { | |
Display.getDefault().asyncExec(() -> setPartName(fChangeInfo.getSubject())); | |
} | |
} | |
}; | |
return subjectListener; | |
} | |
@Override | |
public void doSave(IProgressMonitor monitor) { | |
// ignore | |
} | |
@Override | |
public void doSaveAs() { | |
// ignore | |
} | |
@Override | |
public void init(IEditorSite site, IEditorInput input) throws PartInitException { | |
if (!(input instanceof ChangeDetailEditorInput)) { | |
logger.debug("Input is not a ChangeDetailEditorInput"); //$NON-NLS-1$ | |
} | |
setSite(site); | |
setInput(input); | |
setChangeInfo(((ChangeDetailEditorInput) input).getClient(), ((ChangeDetailEditorInput) input).getChange()); | |
} | |
@Override | |
public boolean isDirty() { | |
// ignore | |
return false; | |
} | |
@Override | |
public boolean isSaveAsAllowed() { | |
// ignore | |
return false; | |
} | |
@Override | |
public void dispose() { | |
checkForUpdates.cancel(); | |
loader.dispose(); | |
fChangeInfo.eAdapters().remove(subjectListener); | |
observableCollector.dispose(); | |
bindingContext.dispose(); | |
deleteDraftRevision.dispose(); | |
patchSetSelector.dispose(); | |
hidableRevertButton.dispose(); | |
hidableSubmitButton.dispose(); | |
historytab.dispose(); | |
messageTab.dispose(); | |
detailsTab.dispose(); | |
} | |
//Job that checks if the review has changed on the server | |
private class CheckForUpdate extends Job { | |
public CheckForUpdate() { | |
super(NLS.bind(Messages.RefreshJobName, fChangeInfo.get_number())); | |
setSystem(true); | |
setPriority(SHORT); | |
} | |
@Override | |
protected IStatus run(IProgressMonitor monitor) { | |
if (monitor.isCanceled()) { | |
return Status.OK_STATUS; | |
} | |
QueryHelpers.loadBasicInformation(fGerritClient, fChangeInfo, false); | |
this.schedule(REFRESH_RATE); | |
return Status.OK_STATUS; | |
} | |
} | |
} |