summaryrefslogtreecommitdiffstats
path: root/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java')
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java560
1 files changed, 560 insertions, 0 deletions
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
new file mode 100644
index 0000000000..cbb29cdb66
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
@@ -0,0 +1,560 @@
+// Copyright (C) 2008 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.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.patches.PatchScreen;
+import com.google.gerrit.client.ui.DirectScreenLink;
+import com.google.gerrit.client.ui.NavigationTable;
+import com.google.gerrit.client.ui.PatchLink;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Patch.Key;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.IncrementalCommand;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.HTMLTable.Cell;
+import com.google.gwtexpui.progress.client.ProgressBar;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+import com.google.gwtorm.client.KeyUtil;
+
+import java.util.List;
+
+public class PatchTable extends Composite {
+ private final FlowPanel myBody;
+ private PatchSet.Id psid;
+ private Command onLoadCommand;
+ private MyTable myTable;
+ private String savePointerId;
+ private List<Patch> patchList;
+
+ public PatchTable() {
+ myBody = new FlowPanel();
+ initWidget(myBody);
+ }
+
+ public void display(final PatchSet.Id id, final List<Patch> list) {
+ psid = id;
+ myTable = null;
+ patchList = list;
+
+ final DisplayCommand cmd = new DisplayCommand(list);
+ if (cmd.execute()) {
+ cmd.initMeter();
+ DeferredCommand.addCommand(cmd);
+ } else {
+ cmd.showTable();
+ }
+ }
+
+ public void setSavePointerId(final String id) {
+ savePointerId = id;
+ }
+
+ public boolean isLoaded() {
+ return myTable != null;
+ }
+
+ public void onTableLoaded(final Command cmd) {
+ if (myTable != null) {
+ cmd.execute();
+ } else {
+ onLoadCommand = cmd;
+ }
+ }
+
+ public void setRegisterKeys(final boolean on) {
+ myTable.setRegisterKeys(on);
+ }
+
+ public void movePointerTo(final Patch.Key k) {
+ myTable.movePointerTo(k);
+ }
+
+ public void notifyDraftDelta(final Patch.Key k, final int delta) {
+ if (myTable != null) {
+ myTable.notifyDraftDelta(k, delta);
+ }
+ }
+
+ /**
+ * @return a link to the previous file in this patch set, or null.
+ */
+ public DirectScreenLink getPreviousPatchLink(int index, PatchScreen.Type patchType) {
+ if (0 < index)
+ return createLink(index - 1, patchType, SafeHtml.asis(Util.C
+ .prevPatchLinkIcon()), null);
+ return null;
+ }
+
+ /**
+ * @return a link to the next file in this patch set, or null.
+ */
+ public DirectScreenLink getNextPatchLink(int index, PatchScreen.Type patchType) {
+ if (index < patchList.size() - 1)
+ return createLink(index + 1, patchType, null, SafeHtml.asis(Util.C
+ .nextPatchLinkIcon()));
+ return null;
+ }
+
+ /**
+ * @return a link to the the given patch.
+ * @param index The patch to link to
+ * @param patchType The type of patch display
+ * @param before A string to display at the beginning of the href text
+ * @param after A string to display at the end of the href text
+ */
+ private PatchLink createLink(int index, PatchScreen.Type patchType,
+ SafeHtml before, SafeHtml after) {
+ Patch patch = patchList.get(index);
+ Key thisKey = patch.getKey();
+ PatchLink link;
+ if (patchType == PatchScreen.Type.SIDE_BY_SIDE
+ && patch.getPatchType() == Patch.PatchType.UNIFIED) {
+ link = new PatchLink.SideBySide("", thisKey, index, this);
+ } else {
+ link = new PatchLink.Unified("", thisKey, index, this);
+ }
+ SafeHtmlBuilder text = new SafeHtmlBuilder();
+ text.append(before);
+ text.append(getFileNameOnly(patch));
+ text.append(after);
+ SafeHtml.set(link, text);
+ return link;
+ }
+
+ private static String getFileNameOnly(Patch patch) {
+ // Note: use '/' here and not File.pathSeparator since git paths
+ // are always separated by /
+ //
+ String fileName = patch.getFileName();
+ int s = fileName.lastIndexOf('/');
+ if (s >= 0) {
+ fileName = fileName.substring(s + 1);
+ }
+ return fileName;
+ }
+
+ /**
+ * Update the reviewed status for the given patch.
+ */
+ public void updateReviewedStatus(Patch.Key patchKey, boolean reviewed) {
+ if (myTable != null) {
+ myTable.updateReviewedStatus(patchKey, reviewed);
+ }
+ }
+
+ private class MyTable extends NavigationTable<Patch> {
+ private static final int C_PATH = 2;
+ private static final int C_DRAFT = 3;
+ private static final int C_SIDEBYSIDE = 4;
+
+ MyTable() {
+ keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.patchTablePrev()));
+ keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.patchTableNext()));
+ keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.patchTableOpen()));
+ keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
+ .patchTableOpen()));
+
+ table.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final Cell cell = table.getCellForEvent(event);
+ if (cell != null && cell.getRowIndex() > 0) {
+ movePointerTo(cell.getRowIndex());
+ }
+ }
+ });
+ setSavePointerId(PatchTable.this.savePointerId);
+ }
+
+ void updateReviewedStatus(final Patch.Key patchKey, boolean reviewed) {
+ final int row = findRow(patchKey);
+ if (0 <= row) {
+ final Patch patch = getRowItem(row);
+ if (patch != null) {
+ patch.setReviewedByCurrentUser(reviewed);
+
+ int col = C_SIDEBYSIDE + 2;
+ if (patch.getPatchType() == Patch.PatchType.BINARY) {
+ col = C_SIDEBYSIDE + 3;
+ }
+
+ if (reviewed) {
+ table.setWidget(row, col, Gerrit.ICONS.greenCheck().createImage());
+ } else {
+ table.clearCell(row, col);
+ }
+ }
+ }
+ }
+
+ void notifyDraftDelta(final Patch.Key key, final int delta) {
+ final int row = findRow(key);
+ if (0 <= row) {
+ final Patch p = getRowItem(row);
+ if (p != null) {
+ p.setDraftCount(p.getDraftCount() + delta);
+ final SafeHtmlBuilder m = new SafeHtmlBuilder();
+ appendCommentCount(m, p);
+ SafeHtml.set(table, row, C_DRAFT, m);
+ }
+ }
+ }
+
+ @Override
+ public void resetHtml(final SafeHtml html) {
+ super.resetHtml(html);
+ }
+
+ @Override
+ public void movePointerTo(Object oldId) {
+ super.movePointerTo(oldId);
+ }
+
+ void initializeRow(int row) {
+ Patch patch = PatchTable.this.patchList.get(row - 1);
+ setRowItem(row, patch);
+
+ Widget nameCol;
+ if (patch.getPatchType() == Patch.PatchType.UNIFIED) {
+ nameCol = new PatchLink.SideBySide(patch.getFileName(), patch.getKey(), row - 1,
+ PatchTable.this);
+ } else {
+ nameCol = new PatchLink.Unified(patch.getFileName(), patch.getKey(), row - 1,
+ PatchTable.this);
+ }
+ if (patch.getSourceFileName() != null) {
+ final String text;
+ if (patch.getChangeType() == Patch.ChangeType.RENAMED) {
+ text = Util.M.renamedFrom(patch.getSourceFileName());
+ } else if (patch.getChangeType() == Patch.ChangeType.COPIED) {
+ text = Util.M.copiedFrom(patch.getSourceFileName());
+ } else {
+ text = Util.M.otherFrom(patch.getSourceFileName());
+ }
+ final Label line = new Label(text);
+ line.setStyleName("SourceFilePath");
+ final FlowPanel cell = new FlowPanel();
+ cell.add(nameCol);
+ cell.add(line);
+ nameCol = cell;
+ }
+ table.setWidget(row, C_PATH, nameCol);
+
+ int C_UNIFIED = C_SIDEBYSIDE + 1;
+ if (patch.getPatchType() == Patch.PatchType.UNIFIED) {
+ table.setWidget(row, C_SIDEBYSIDE,
+ new PatchLink.SideBySide(Util.C.patchTableDiffSideBySide(), patch.getKey(), row - 1,
+ PatchTable.this));
+
+ } else if (patch.getPatchType() == Patch.PatchType.BINARY) {
+ C_UNIFIED = C_SIDEBYSIDE + 2;
+ }
+ table.setWidget(row, C_UNIFIED,
+ new PatchLink.Unified(Util.C.patchTableDiffUnified(), patch.getKey(), row - 1,
+ PatchTable.this));
+ }
+
+ void appendHeader(final SafeHtmlBuilder m) {
+ m.openTr();
+
+ // Cursor
+ m.openTd();
+ m.addStyleName(S_ICON_HEADER);
+ m.addStyleName("LeftMostCell");
+ m.nbsp();
+ m.closeTd();
+
+ // Mode
+ m.openTd();
+ m.setStyleName(S_ICON_HEADER);
+ m.nbsp();
+ m.closeTd();
+
+ // "File path"
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.append(Util.C.patchTableColumnName());
+ m.closeTd();
+
+ // "Comments"
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.append(Util.C.patchTableColumnComments());
+ m.closeTd();
+
+ // "Diff"
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.setAttribute("colspan", 3);
+ m.append(Util.C.patchTableColumnDiff());
+ m.closeTd();
+
+ // "Reviewed"
+ if (Gerrit.isSignedIn()) {
+ m.openTd();
+ m.setStyleName(S_ICON_HEADER);
+ m.append(Util.C.reviewed());
+ m.closeTd();
+ }
+
+ m.closeTr();
+ }
+
+ void appendRow(final SafeHtmlBuilder m, final Patch p) {
+ m.openTr();
+
+ m.openTd();
+ m.addStyleName(S_ICON_CELL);
+ m.addStyleName("LeftMostCell");
+ m.nbsp();
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName("ChangeTypeCell");
+ m.append(p.getChangeType().getCode());
+ m.closeTd();
+
+ m.openTd();
+ m.addStyleName(S_DATA_CELL);
+ m.addStyleName("FilePathCell");
+ m.closeTd();
+
+ m.openTd();
+ m.addStyleName(S_DATA_CELL);
+ m.addStyleName("CommentCell");
+ appendCommentCount(m, p);
+ m.closeTd();
+
+ switch (p.getPatchType()) {
+ case UNIFIED:
+ openlink(m, 2);
+ m.closeTd();
+ break;
+
+ case BINARY: {
+ String base = GWT.getHostPageBaseURL();
+ base += "cat/" + KeyUtil.encode(p.getKey().toString());
+ switch (p.getChangeType()) {
+ case DELETED:
+ case MODIFIED:
+ openlink(m, 1);
+ m.openAnchor();
+ m.setAttribute("href", base + "^1");
+ m.append(Util.C.patchTableDownloadPreImage());
+ closelink(m);
+ break;
+ default:
+ emptycell(m, 1);
+ break;
+ }
+ switch (p.getChangeType()) {
+ case MODIFIED:
+ case ADDED:
+ openlink(m, 1);
+ m.openAnchor();
+ m.setAttribute("href", base + "^0");
+ m.append(Util.C.patchTableDownloadPostImage());
+ closelink(m);
+ break;
+ default:
+ emptycell(m, 1);
+ break;
+ }
+ break;
+ }
+
+ default:
+ emptycell(m, 2);
+ break;
+ }
+
+ openlink(m, 1);
+ m.closeTd();
+
+ // Green check mark if the user is logged in and they reviewed that file
+ if (Gerrit.isSignedIn()) {
+ m.openTd();
+ m.setStyleName(S_DATA_CELL);
+ if (p.isReviewedByCurrentUser()) {
+ m.append(SafeHtml.asis(Gerrit.ICONS.greenCheck().getHTML()));
+ }
+ m.closeTd();
+ }
+
+ m.closeTr();
+ }
+
+ void appendCommentCount(final SafeHtmlBuilder m, final Patch p) {
+ if (p.getCommentCount() > 0) {
+ m.append(Util.M.patchTableComments(p.getCommentCount()));
+ }
+ if (p.getDraftCount() > 0) {
+ if (p.getCommentCount() > 0) {
+ m.append(", ");
+ }
+ m.openSpan();
+ m.setStyleName("Drafts");
+ m.append(Util.M.patchTableDrafts(p.getDraftCount()));
+ m.closeSpan();
+ }
+ }
+
+ private void openlink(final SafeHtmlBuilder m, final int colspan) {
+ m.openTd();
+ m.addStyleName(S_DATA_CELL);
+ m.addStyleName("DiffLinkCell");
+ m.setAttribute("colspan", colspan);
+ }
+
+ private void closelink(final SafeHtmlBuilder m) {
+ m.closeAnchor();
+ m.closeTd();
+ }
+
+ private void emptycell(final SafeHtmlBuilder m, final int colspan) {
+ m.openTd();
+ m.addStyleName(S_DATA_CELL);
+ m.addStyleName("DiffLinkCell");
+ m.setAttribute("colspan", colspan);
+ m.nbsp();
+ m.closeTd();
+ }
+
+ @Override
+ protected Object getRowItemKey(final Patch item) {
+ return item.getKey();
+ }
+
+ @Override
+ protected void onOpenRow(final int row) {
+ Widget link = table.getWidget(row, C_PATH);
+ if (link instanceof FlowPanel) {
+ link = ((FlowPanel) link).getWidget(0);
+ }
+ if (link instanceof DirectScreenLink) {
+ ((DirectScreenLink) link).go();
+ }
+ }
+ }
+
+ private final class DisplayCommand implements IncrementalCommand {
+ private final MyTable table;
+ private final List<Patch> list;
+ private boolean attached;
+ private SafeHtmlBuilder nc = new SafeHtmlBuilder();
+ private int stage = 0;
+ private int row;
+ private double start;
+ private ProgressBar meter;
+
+ private DisplayCommand(final List<Patch> list) {
+ this.table = new MyTable();
+ this.list = list;
+ }
+
+ /**
+ * Add the files contained in the list of patches to the table, one per row.
+ */
+ @SuppressWarnings("fallthrough")
+ public boolean execute() {
+ final boolean attachedNow = isAttached();
+ if (!attached && attachedNow) {
+ // Remember that we have been attached at least once. If
+ // later we find we aren't attached we should stop running.
+ //
+ attached = true;
+ } else if (attached && !attachedNow) {
+ // If the user navigated away, we aren't in the DOM anymore.
+ // Don't continue to render.
+ //
+ return false;
+ }
+
+ start = System.currentTimeMillis();
+ switch (stage) {
+ case 0:
+ if (row == 0) {
+ table.appendHeader(nc);
+ }
+ while (row < list.size()) {
+ table.appendRow(nc, list.get(row));
+ if ((++row % 10) == 0 && longRunning()) {
+ updateMeter();
+ return true;
+ }
+ }
+ table.resetHtml(nc);
+ nc = null;
+ stage = 1;
+ row = 0;
+
+ case 1:
+ while (row < list.size()) {
+ table.initializeRow(row + 1);
+ if ((++row % 50) == 0 && longRunning()) {
+ updateMeter();
+ return true;
+ }
+ }
+ updateMeter();
+ showTable();
+ }
+ return false;
+ }
+
+ void showTable() {
+ PatchTable.this.myBody.clear();
+ PatchTable.this.myBody.add(table);
+ PatchTable.this.myTable = table;
+ table.finishDisplay();
+ if (PatchTable.this.onLoadCommand != null) {
+ PatchTable.this.onLoadCommand.execute();
+ PatchTable.this.onLoadCommand = null;
+ }
+ }
+
+ void initMeter() {
+ if (meter == null) {
+ meter = new ProgressBar(Util.M.loadingPatchSet(psid.get()));
+ PatchTable.this.myBody.clear();
+ PatchTable.this.myBody.add(meter);
+ }
+ updateMeter();
+ }
+
+ void updateMeter() {
+ if (meter != null) {
+ final int n = list.size();
+ meter.setValue(((100 * (stage * n + row)) / (2 * n)));
+ }
+ }
+
+ private boolean longRunning() {
+ return System.currentTimeMillis() - start > 200;
+ }
+ }
+
+}