// Copyright (C) 2013 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.change; import com.google.gerrit.client.FormatUtil; import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.changes.ChangeApi; import com.google.gerrit.client.changes.ChangeList; import com.google.gerrit.client.info.ChangeInfo; import com.google.gerrit.client.info.ChangeInfo.CommitInfo; import com.google.gerrit.client.info.ChangeInfo.EditInfo; import com.google.gerrit.client.info.ChangeInfo.RevisionInfo; 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.FancyFlexTableImpl; import com.google.gerrit.common.PageLinks; import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.reviewdb.client.Change; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JsArray; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.resources.client.CssResource; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.EventListener; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.impl.HyperlinkImpl; import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder; import java.util.Collections; import java.util.EnumSet; class PatchSetsBox extends Composite { interface Binder extends UiBinder<HTMLPanel, PatchSetsBox> {} private static final Binder uiBinder = GWT.create(Binder.class); private static final String OPEN; private static final HyperlinkImpl link = GWT.create(HyperlinkImpl.class); static { OPEN = DOM.createUniqueId().replace('-', '_'); init(OPEN); } private static native void init(String o) /*-{ $wnd[o] = $entry(function(e,i) { return @com.google.gerrit.client.change.PatchSetsBox::onOpen(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i); }); }-*/; private static boolean onOpen(NativeEvent e, int idx) { if (link.handleAsClick(e.<Event>cast())) { PatchSetsBox t = getRevisionBox(e); if (t != null) { t.onOpenRow(idx); e.preventDefault(); return false; } } return true; } private static PatchSetsBox getRevisionBox(NativeEvent event) { Element e = event.getEventTarget().cast(); for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) { EventListener l = DOM.getEventListener(e); if (l instanceof PatchSetsBox) { return (PatchSetsBox) l; } } return null; } interface Style extends CssResource { String current(); String legacy_id(); String commit(); String draft_comment(); } private final Change.Id changeId; private final String revision; private final EditInfo edit; private boolean loaded; private JsArray<RevisionInfo> revisions; @UiField FlexTable table; @UiField Style style; PatchSetsBox(Change.Id changeId, String revision, EditInfo edit) { this.changeId = changeId; this.revision = revision; this.edit = edit; initWidget(uiBinder.createAndBindUi(this)); } @Override protected void onLoad() { if (!loaded) { RestApi call = ChangeApi.detail(changeId.get()); ChangeList.addOptions( call, EnumSet.of(ListChangesOption.ALL_COMMITS, ListChangesOption.ALL_REVISIONS)); call.get( new AsyncCallback<ChangeInfo>() { @Override public void onSuccess(ChangeInfo result) { if (edit != null) { edit.setName(edit.commit().commit()); result.revisions().put(edit.name(), RevisionInfo.fromEdit(edit)); } render(result.revisions()); loaded = true; } @Override public void onFailure(Throwable caught) {} }); } } private void onOpenRow(int idx) { closeParent(); Gerrit.display(url(revisions.get(idx))); } private void render(NativeMap<RevisionInfo> map) { map.copyKeysIntoChildren("name"); revisions = map.values(); RevisionInfo.sortRevisionInfoByNumber(revisions); Collections.reverse(Natives.asList(revisions)); SafeHtmlBuilder sb = new SafeHtmlBuilder(); header(sb); for (int i = 0; i < revisions.length(); i++) { revision(sb, i, revisions.get(i)); } GWT.<FancyFlexTableImpl>create(FancyFlexTableImpl.class).resetHtml(table, sb); } private void header(SafeHtmlBuilder sb) { sb.openTr() .openTh() .setStyleName(style.legacy_id()) .append(Resources.C.patchSet()) .closeTh() .openTh() .append(Resources.C.commit()) .closeTh() .openTh() .append(Resources.C.date()) .closeTh() .openTh() .append(Resources.C.author()) .closeTh() .closeTr(); } private void revision(SafeHtmlBuilder sb, int index, RevisionInfo r) { CommitInfo c = r.commit(); sb.openTr(); if (revision.equals(r.name())) { sb.setStyleName(style.current()); } sb.openTd().setStyleName(style.legacy_id()); if (r.draft()) { sb.append(Resources.C.draft()).append(' '); } sb.append(r.id()); sb.closeTd(); sb.openTd() .setStyleName(style.commit()) .openAnchor() .setAttribute("href", "#" + url(r)) .setAttribute("onclick", OPEN + "(event," + index + ")") .append(r.name().substring(0, 10)) .closeAnchor() .closeTd(); sb.openTd().append(FormatUtil.shortFormatDayTime(c.committer().date())).closeTd(); String an = c.author() != null ? c.author().name() : ""; String cn = c.committer() != null ? c.committer().name() : ""; sb.openTd(); sb.append(an); if (!"".equals(an) && !"".equals(cn) && !an.equals(cn)) { sb.append(" / ").append(cn); } sb.closeTd(); sb.closeTr(); } private String url(RevisionInfo r) { return PageLinks.toChange(changeId, r.id()); } private void closeParent() { for (Widget w = getParent(); w != null; w = w.getParent()) { if (w instanceof PopupPanel) { ((PopupPanel) w).hide(true); break; } } } }