blob: eec3d0cd6b4b3ebe8fd1b4597625e8f1710664a0 [file] [log] [blame]
// Copyright (C) 2010 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.diff;
import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ReviewInfo;
import com.google.gerrit.client.changes.Util;
import com.google.gerrit.client.diff.DiffInfo.Region;
import com.google.gerrit.client.info.ChangeInfo;
import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.info.FileInfo;
import com.google.gerrit.client.info.GitwebInfo;
import com.google.gerrit.client.info.WebLinkInfo;
import com.google.gerrit.client.patches.PatchUtil;
import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.client.ui.InlineHyperlink;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwtexpui.globalkey.client.KeyCommand;
import com.google.gwtexpui.globalkey.client.KeyCommandSet;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import java.util.List;
public class Header extends Composite {
interface Binder extends UiBinder<HTMLPanel, Header> {}
private static final Binder uiBinder = GWT.create(Binder.class);
static {
Resources.I.style().ensureInjected();
}
private static enum ReviewedState {
AUTO_REVIEW, LOADED
}
@UiField CheckBox reviewed;
@UiField Element project;
@UiField Element filePath;
@UiField Element noDiff;
@UiField FlowPanel linkPanel;
@UiField InlineHyperlink prev;
@UiField InlineHyperlink up;
@UiField InlineHyperlink next;
@UiField Image preferences;
private final KeyCommandSet keys;
private final PatchSet.Id base;
private final PatchSet.Id patchSetId;
private final String path;
private boolean hasPrev;
private boolean hasNext;
private String nextPath;
private PreferencesAction prefsAction;
private ReviewedState reviewedState;
Header(KeyCommandSet keys, PatchSet.Id base, PatchSet.Id patchSetId,
String path) {
initWidget(uiBinder.createAndBindUi(this));
this.keys = keys;
this.base = base;
this.patchSetId = patchSetId;
this.path = path;
if (!Gerrit.isSignedIn()) {
reviewed.getElement().getStyle().setVisibility(Visibility.HIDDEN);
}
SafeHtml.setInnerHTML(filePath, formatPath(path, null, null));
up.setTargetHistoryToken(PageLinks.toChange(
patchSetId.getParentKey(),
base != null ? base.getId() : null, patchSetId.getId()));
}
public static SafeHtml formatPath(String path, String project, String commit) {
SafeHtmlBuilder b = new SafeHtmlBuilder();
if (Patch.COMMIT_MSG.equals(path)) {
return b.append(Util.C.commitMessage());
}
GitwebInfo gw = (project != null && commit != null)
? Gerrit.info().gitweb() : null;
int s = path.lastIndexOf('/') + 1;
if (gw != null && s > 0) {
String base = path.substring(0, s - 1);
b.openAnchor()
.setAttribute("href", gw.toFile(project, commit, base))
.setAttribute("title", gw.getLinkName())
.append(base)
.closeAnchor()
.append('/');
} else {
b.append(path.substring(0, s));
}
b.openElement("b");
b.append(path.substring(s));
b.closeElement("b");
return b;
}
@Override
protected void onLoad() {
DiffApi.list(patchSetId, base, new GerritCallback<NativeMap<FileInfo>>() {
@Override
public void onSuccess(NativeMap<FileInfo> result) {
JsArray<FileInfo> files = result.values();
FileInfo.sortFileInfoByPath(files);
int index = 0; // TODO: Maybe use patchIndex.
for (int i = 0; i < files.length(); i++) {
if (path.equals(files.get(i).path())) {
index = i;
break;
}
}
FileInfo nextInfo = index == files.length() - 1
? null
: files.get(index + 1);
KeyCommand p = setupNav(prev, '[', PatchUtil.C.previousFileHelp(),
index == 0 ? null : files.get(index - 1));
KeyCommand n = setupNav(next, ']', PatchUtil.C.nextFileHelp(),
nextInfo);
if (p != null && n != null) {
keys.pair(p, n);
}
nextPath = nextInfo != null ? nextInfo.path() : null;
}
});
if (Gerrit.isSignedIn()) {
ChangeApi.revision(patchSetId).view("files")
.addParameterTrue("reviewed")
.get(new AsyncCallback<JsArrayString>() {
@Override
public void onSuccess(JsArrayString result) {
boolean b = Natives.asList(result).contains(path);
reviewed.setValue(b, false);
if (!b && reviewedState == ReviewedState.AUTO_REVIEW) {
postAutoReviewed();
}
reviewedState = ReviewedState.LOADED;
}
@Override
public void onFailure(Throwable caught) {
}
});
}
}
void autoReview() {
if (reviewedState == ReviewedState.LOADED && !reviewed.getValue()) {
postAutoReviewed();
} else {
reviewedState = ReviewedState.AUTO_REVIEW;
}
}
void setChangeInfo(ChangeInfo info) {
GitwebInfo gw = Gerrit.info().gitweb();
if (gw != null) {
for (RevisionInfo rev : Natives.asList(info.revisions().values())) {
if (patchSetId.getId().equals(rev.id())) {
String c = rev.name();
SafeHtml.setInnerHTML(filePath, formatPath(path, info.project(), c));
SafeHtml.setInnerHTML(project, new SafeHtmlBuilder()
.openAnchor()
.setAttribute("href", gw.toFile(info.project(), c, ""))
.setAttribute("title", gw.getLinkName())
.append(info.project())
.closeAnchor());
return;
}
}
}
project.setInnerText(info.project());
}
void init(PreferencesAction pa, List<InlineHyperlink> links,
List<WebLinkInfo> webLinks) {
prefsAction = pa;
prefsAction.setPartner(preferences);
for (InlineHyperlink link : links) {
linkPanel.add(link);
}
for (WebLinkInfo webLink : webLinks) {
linkPanel.add(webLink.toAnchor());
}
}
@UiHandler("reviewed")
void onValueChange(ValueChangeEvent<Boolean> event) {
if (event.getValue()) {
reviewed().put(CallbackGroup.<ReviewInfo> emptyCallback());
} else {
reviewed().delete(CallbackGroup.<ReviewInfo> emptyCallback());
}
}
private void postAutoReviewed() {
reviewed().background().put(new AsyncCallback<ReviewInfo>() {
@Override
public void onSuccess(ReviewInfo result) {
reviewed.setValue(true, false);
}
@Override
public void onFailure(Throwable caught) {
}
});
}
private RestApi reviewed() {
return ChangeApi.revision(patchSetId)
.view("files")
.id(path)
.view("reviewed");
}
@UiHandler("preferences")
void onPreferences(@SuppressWarnings("unused") ClickEvent e) {
prefsAction.show();
}
private String url(FileInfo info) {
return info.binary()
? Dispatcher.toUnified(base, patchSetId, info.path())
: Dispatcher.toSideBySide(base, patchSetId, info.path());
}
private KeyCommand setupNav(InlineHyperlink link, char key, String help, FileInfo info) {
if (info != null) {
final String url = url(info);
link.setTargetHistoryToken(url);
link.setTitle(PatchUtil.M.fileNameWithShortcutKey(
FileInfo.getFileName(info.path()),
Character.toString(key)));
KeyCommand k = new KeyCommand(0, key, help) {
@Override
public void onKeyPress(KeyPressEvent event) {
Gerrit.display(url);
}
};
keys.add(k);
if (link == prev) {
hasPrev = true;
} else {
hasNext = true;
}
return k;
} else {
link.getElement().getStyle().setVisibility(Visibility.HIDDEN);
keys.add(new UpToChangeCommand(patchSetId, 0, key));
return null;
}
}
Runnable toggleReviewed() {
return new Runnable() {
@Override
public void run() {
reviewed.setValue(!reviewed.getValue(), true);
}
};
}
Runnable navigate(Direction dir) {
switch (dir) {
case PREV:
return new Runnable() {
@Override
public void run() {
(hasPrev ? prev : up).go();
}
};
case NEXT:
return new Runnable() {
@Override
public void run() {
(hasNext ? next : up).go();
}
};
default:
return new Runnable() {
@Override
public void run() {
}
};
}
}
Runnable reviewedAndNext() {
return new Runnable() {
@Override
public void run() {
if (Gerrit.isSignedIn()) {
reviewed.setValue(true, true);
}
navigate(Direction.NEXT).run();
}
};
}
String getNextPath() {
return nextPath;
}
void setNoDiff(DiffInfo diff) {
JsArray<Region> regions = diff.content();
boolean b = regions.length() == 0
|| (regions.length() == 1 && regions.get(0).ab() != null);
UIObject.setVisible(noDiff, b);
}
}