blob: ba9d4f24de5a78e2793add3747e4353e7dbb9c77 [file] [log] [blame]
/*******************************************************************************
* 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:
* Jacques Bouthillier - Initial Implementation of the plug-in utility
******************************************************************************/
package org.eclipse.egerrit.internal.ui.utils;
import java.text.SimpleDateFormat;
import java.util.concurrent.CompletableFuture;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.egerrit.internal.core.EGerritCorePlugin;
import org.eclipse.egerrit.internal.core.GerritClient;
import org.eclipse.egerrit.internal.core.command.CreateDraftCommand;
import org.eclipse.egerrit.internal.core.command.SetReviewCommand;
import org.eclipse.egerrit.internal.core.exception.EGerritException;
import org.eclipse.egerrit.internal.core.rest.ReviewInput;
import org.eclipse.egerrit.internal.core.utils.Utils;
import org.eclipse.egerrit.internal.model.ChangeInfo;
import org.eclipse.egerrit.internal.model.ChangeMessageInfo;
import org.eclipse.egerrit.internal.model.CommentInfo;
import org.eclipse.egerrit.internal.model.FileInfo;
import org.eclipse.egerrit.internal.model.ModelFactory;
import org.eclipse.egerrit.internal.model.ModelHelpers;
import org.eclipse.egerrit.internal.model.RevisionInfo;
import org.eclipse.egerrit.internal.ui.EGerritUIPlugin;
import org.eclipse.egerrit.internal.ui.compare.CommentPrettyPrinter;
import org.eclipse.egerrit.internal.ui.editors.ChangeDetailEditor;
import org.eclipse.egerrit.internal.ui.editors.OpenCompareEditor;
import org.eclipse.egerrit.internal.ui.editors.QueryHelpers;
import org.eclipse.egerrit.internal.ui.editors.ReplyDialog;
import org.eclipse.egerrit.internal.ui.editors.model.ChangeDetailEditorInput;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.ITextEditor;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class implements the Gerrit UI utility.
*
* @since 1.0
*/
public class UIUtils {
private static Logger logger = LoggerFactory.getLogger(UIUtils.class);
private static final String EGERRIT_PREF = "org.eclipse.egerrit.prefs"; //$NON-NLS-1$
private static final int TITLE_LENGTH = 75;
/**
* The default constructor. Do not allow to build an object of this class
*/
private UIUtils() {
}
/**
* To display some information to the end-user
*
* @param title
* @param message
*/
public static void displayInformation(final String title, final String message) {
PlatformUI.getWorkbench().getDisplay().syncExec(() -> MessageDialog.openInformation(null, title, message));
}
/**
* Compute the size of the Fonts used for this composite
*
* @param composite
* @return Point
*/
public static Point computeFontSize(Composite composite) {
GC gc = new GC(composite);
FontMetrics fm = gc.getFontMetrics();
int charWidth = fm.getAverageCharWidth();
gc.dispose();
return new Point(charWidth, fm.getHeight());
}
/**
* Remove the link argument and return the text
*
* @return String
*/
public static String getLinkText(String text) {
text = text.replaceFirst("<a>", ""); //$NON-NLS-1$ //$NON-NLS-2$
text = text.replaceFirst("</a>", ""); //$NON-NLS-1$//$NON-NLS-2$
return text.trim();
}
public static void replyToChange(Shell shell, RevisionInfo revisionInfo, String reason, GerritClient client,
boolean waitForDataRefresh, ChangeMessageInfo messageInfo) {
String current = revisionInfo.getId();
QueryHelpers.loadDrafts(client, revisionInfo); //Force load the drafts to make sure they are shown in the dialog.
final ReplyDialog replyDialog = new ReplyDialog(shell, reason, revisionInfo, client, messageInfo);
Display.getDefault().syncExec(() -> {
int ret = replyDialog.open();
if (ret == IDialogConstants.OK_ID) {
//Fill the data structure for the reply
ReviewInput reviewInput = new ReviewInput();
reviewInput.setMessage(replyDialog.getValue());
reviewInput.setLabels(replyDialog.getRadiosSelection());
reviewInput.setDrafts(ReviewInput.DRAFT_PUBLISH);
CompletableFuture<Void> reloading = CompletableFuture.runAsync(() -> {
try {
SetReviewCommand reviewToEmit = client.setReview(revisionInfo.getChangeInfo().getId(), current);
reviewToEmit.setCommandInput(reviewInput);
reviewToEmit.call();
} catch (EGerritException e1) {
EGerritCorePlugin.logError(client.getRepository().formatGerritVersion() + e1.getMessage());
}
}).thenRun(() -> {
//Here we don't use the model loader because we need the re-loading of the comments to be done synchronously
QueryHelpers.loadBasicInformation(client, revisionInfo.getChangeInfo(), false);
});
if (waitForDataRefresh) {
reloading.join();
}
}
});
}
public static String revisionToString(RevisionInfo revisionInfo) {
StringBuilder sb = new StringBuilder();
sb.append(Integer.toString(revisionInfo.get_number())); //
sb.append(" "); //$NON-NLS-1$
appendCommitTime(sb, revisionInfo);
sb.append(" "); //$NON-NLS-1$
appendUserCommiter(sb, revisionInfo);
if (revisionInfo.isDraft()) {
sb.append(Messages.UIUtils_0);
}
return sb.toString();
}
public static void postReply(GerritClient gerritClient, CommentInfo comment, String reply, String revisionId) {
if (reply.trim().length() == 0) {
return;
}
CreateDraftCommand publishDraft = gerritClient
.createDraftComments(ModelHelpers.getRevision(comment).getChangeInfo().getId(), revisionId);
CommentInfo replyData = ModelFactory.eINSTANCE.createCommentInfo();
replyData.setMessage(reply);
replyData.setInReplyTo(comment.getId());
replyData.setLine(comment.getLine());
replyData.setSide(comment.getSide());
replyData.setPath(ModelHelpers.getFileInfo(comment).getPath());
publishDraft.setCommandInput(replyData);
try {
ModelHelpers.getFileInfo(comment).getDraftComments().add(publishDraft.call());
} catch (EGerritException e) {
EGerritCorePlugin.logError(gerritClient.getRepository().formatGerritVersion() + e.getMessage());
}
}
private static void appendCommitTime(StringBuilder sb, RevisionInfo revisionInfo) {
if (revisionInfo.getCommit() != null) {
sb.append(Utils.formatDate(revisionInfo.getCommit().getCommitter().getDate(),
new SimpleDateFormat("MMM dd, yyyy hh:mm a"))); //$NON-NLS-1$
}
}
private static void appendUserCommiter(StringBuilder sb, RevisionInfo revisionInfo) {
if (revisionInfo.getCommit() != null) {
if (revisionInfo.getCommit().getAuthor() != null) {
//Use the Author
sb.append(revisionInfo.getCommit().getAuthor().getName());
}
if (!revisionInfo.getCommit()
.getAuthor()
.getName()
.equals(revisionInfo.getCommit().getCommitter().getName())) {
//Add the committer if different than the Author
sb.append("/"); //$NON-NLS-1$
sb.append(revisionInfo.getCommit().getCommitter().getName());
}
}
}
public static String formatMessageForMarkerView(CommentInfo commentInfo, int newPosition) {
String psString = getPatchSetString(commentInfo);
String author = commentInfo.getAuthor() != null ? commentInfo.getAuthor().getName() : null;
String message = commentInfo.getMessage();
boolean lineDeleted = newPosition < 0;
String formattedMessage;
if (author == null) {
//This is a draft
formattedMessage = NLS.bind(Messages.UIUtils_draft,
new String[] { message, CommentPrettyPrinter.printDate(commentInfo), psString });
} else {
formattedMessage = NLS.bind(Messages.UIUtils_comment,
new String[] { message, author, CommentPrettyPrinter.printDate(commentInfo), psString });
}
if (lineDeleted) {
return formattedMessage + NLS.bind(Messages.UIUtils_lineDeleted, commentInfo.getLine());
}
return formattedMessage;
}
public static String formatMessageForQuickFix(CommentInfo commentInfo) {
String message = commentInfo.getMessage();
if (message.length() < 20) {
return message;
}
int nextSpace = message.indexOf(' ', 20);
if (nextSpace == -1) {
return message;
}
return message.substring(0, nextSpace) + " ..."; //$NON-NLS-1$
}
public static String getPatchSetString(CommentInfo comment) {
return getPatchSetString(ModelHelpers.getRevision(comment));
}
public static String getPatchSetString(RevisionInfo revision) {
return revision.get_number() + "/" //$NON-NLS-1$
+ ModelHelpers.getHighestRevisionNumber(revision.getChangeInfo().getRevisions().values());
}
/**
* Open a compare editor against the another version of the file
*
* @param key
* @param gerritClient
* @param fileInfo
* @param changeInfo
* @param leftSide
*/
public static void open(GerritClient gerritClient, FileInfo fileInfo, ChangeInfo changeInfo, String leftSide) {
OpenCompareEditor compareEditor = new OpenCompareEditor(gerritClient, changeInfo);
compareEditor.compareFiles(leftSide, fileInfo.getRevision().getId(), fileInfo);
}
/**
* Open a file in a single editor
*
* @param key
* @param revInfo
* @param line
* @return
*/
public static boolean openSingleFile(FileInfo fileToShow, GerritClient gerritClient, RevisionInfo revInfo,
int line) {
if (fileToShow == null) {
return false;
}
IFile workspaceFile = new OpenCompareEditor(gerritClient, revInfo.getChangeInfo())
.getCorrespondingWorkspaceFile(fileToShow);
return openSingleFile(workspaceFile, line);
}
public static boolean openSingleFile(IResource resource, int line) {
IFile workspaceFile = (resource instanceof IFile) ? (IFile) resource : null;
if (workspaceFile == null) {
return false;
}
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
try {
IEditorPart editor = IDE.openEditor(page, workspaceFile);
if (editor != null && line != 0) {
selectLine(editor, line);
}
} catch (PartInitException e1) {
logger.debug("Could not open the editor for " + workspaceFile.getFullPath(), e1); //$NON-NLS-1$
}
return true;
}
private static void selectLine(IEditorPart editorPart, int selectedLine) {
if (editorPart instanceof ITextEditor) {
ITextEditor editor = (ITextEditor) editorPart;
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
if (document != null) {
try {
IRegion line = document.getLineInformation(selectedLine);
editor.selectAndReveal(line.getOffset(), 0);
} catch (BadLocationException e) {
// line seems not to exist in
// workspace version
}
}
}
}
/**
* Show an EGerrit tip to the end-user until the toggle is selected
*
* @param key
* @param shell
* @param title
* @param value
*/
public static void showDialogTip(String key, Shell shell, String title, String value, String note) {
Preferences prefs = ConfigurationScope.INSTANCE.getNode(EGERRIT_PREF);
Preferences editorPrefs = prefs.node(key);
boolean choice = editorPrefs.getBoolean(key, false);
if (choice) {
if (note != null) {
//always show a dialog with the note here
MessageDialog.openInformation(shell, Messages.UIUtils_EGerriNoteTitle, note);
}
return;
}
//Keep the title length to TITLE_LENGTH characters max
if (title.length() > TITLE_LENGTH) {
title = title.substring(0, TITLE_LENGTH - 3).concat("..."); //$NON-NLS-1$
}
if (note != null) {
value = value + "\n\n" + note; //$NON-NLS-1$
}
MessageDialogWithToggle dialog = MessageDialogWithToggle.openInformation(shell, title, value,
Messages.UIUtils_EGerriTipShowAgain, false, null, null);
if (dialog.getToggleState()) {
editorPrefs.putBoolean(key, true);
try {
editorPrefs.flush();
} catch (BackingStoreException e) {
//There is not much we can do
}
}
return;
}
/**
* Show a dialog allowing the user to by-pass the dialogue when renaming a branch is available until the toggle is
* selected
*
* @param key
* @param shell
* @param title
* @param value
* @return
*/
public static int renameBranch(String key, Shell shell, String title, String message) {
Preferences prefs = ConfigurationScope.INSTANCE.getNode(EGERRIT_PREF);
Preferences editorPrefs = prefs.node(key);
boolean choice = editorPrefs.getBoolean(key, false);
if (choice) {
return IDialogConstants.YES_ID;
}
MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(shell, title, message,
Messages.UIUtils_EGerriTipRenameShowAgain, false, null, null);
if (dialog.getToggleState()) {
editorPrefs.putBoolean(key, true);
try {
editorPrefs.flush();
} catch (BackingStoreException e) {
//There is not much we can do
}
}
return dialog.getReturnCode();
}
/*********************************************/
/**
* Open editor with the newest changeInfo
*
* @param changeInfo
* @param gerritClient
*/
public static void openAnotherEditor(ChangeInfo changeInfo, GerritClient fGerritClient) {
IWorkbench workbench = EGerritUIPlugin.getDefault().getWorkbench();
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
IWorkbenchPage page = null;
if (window != null) {
page = workbench.getActiveWorkbenchWindow().getActivePage();
}
if (page != null) {
try {
IEditorInput input = new ChangeDetailEditorInput(fGerritClient, changeInfo);
IEditorPart reusedEditor = page.findEditor(input);
page.openEditor(input, ChangeDetailEditor.EDITOR_ID);
if (reusedEditor instanceof ChangeDetailEditor) {
((ChangeDetailEditor) reusedEditor).refreshStatus();
}
} catch (PartInitException e) {
EGerritCorePlugin.logError(fGerritClient != null
? fGerritClient.getRepository().formatGerritVersion() + e.getMessage()
: e.getMessage());
}
}
}
/**
* Create a acronym label based on the provided label
*
* @param label
* @return
*/
public static String getAcronymLabel(String label) {
String ret = ""; //$NON-NLS-1$
if (!label.isEmpty()) {
String[] arraySt = label.split("-"); //$NON-NLS-1$
for (String s : arraySt) {
ret = ret.concat(s.substring(0, 1));
}
}
return ret;
}
}