| // 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.extensions.client.GeneralPreferencesInfo.DiffView; |
| 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 enum ReviewedState { |
| AUTO_REVIEW, LOADED |
| } |
| |
| @UiField CheckBox reviewed; |
| @UiField Element project; |
| @UiField Element filePath; |
| @UiField Element fileNumber; |
| @UiField Element fileCount; |
| |
| @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 final DiffView diffScreenType; |
| 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, DiffView diffSreenType) { |
| initWidget(uiBinder.createAndBindUi(this)); |
| this.keys = keys; |
| this.base = base; |
| this.patchSetId = patchSetId; |
| this.path = path; |
| this.diffScreenType = diffSreenType; |
| |
| 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); |
| fileNumber.setInnerText( |
| Integer.toString(Natives.asList(files).indexOf(result.get(path)) + 1)); |
| fileCount.setInnerText(Integer.toString(files.length())); |
| 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 diffScreenType == DiffView.UNIFIED_DIFF |
| ? 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) { |
| if (diff.binary()) { |
| UIObject.setVisible(noDiff, false); // Don't bother showing "No Differences" |
| } else { |
| JsArray<Region> regions = diff.content(); |
| boolean b = regions.length() == 0 |
| || (regions.length() == 1 && regions.get(0).ab() != null); |
| UIObject.setVisible(noDiff, b); |
| } |
| } |
| } |