blob: 85bda4c3842f251ae67b814178e37df623baf675 [file] [log] [blame]
// 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.patches;
import com.google.gerrit.client.data.SideBySideLine;
import com.google.gerrit.client.data.SideBySidePatchDetail;
import com.google.gerrit.client.reviewdb.PatchLineComment;
import com.google.gerrit.client.ui.ComplexDisclosurePanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import java.util.Iterator;
import java.util.List;
public class SideBySideTable extends AbstractPatchContentTable {
private int fileCnt;
private int maxLineNumber;
protected int getFileCount() {
return fileCnt;
}
protected String getFileTitle(int file) {
return table.getText(0, 1 + file * 2 + 1);
}
@Override
protected void onCellDoubleClick(final int row, int column) {
if (column > 0 && getRowItem(row) instanceof SideBySideLineList) {
final SideBySideLineList pl = (SideBySideLineList) getRowItem(row);
final short file = (short) ((column - 1) / 2);
if (column < (1 + file * 2 + 1)) {
column++;
}
final SideBySideLine line = pl.lines.get(file);
switch (line.getType()) {
case DELETE:
case EQUAL:
case INSERT: {
createCommentEditor(row + 1, column, line.getLineNumber(), file);
break;
}
}
}
}
@Override
protected void onOpenItem(final Object item) {
if (item instanceof SideBySideLineList) {
final SideBySideLineList pl = (SideBySideLineList) item;
final short file = (short) (pl.lines.size() - 1);
final int row = getCurrentRow();
final int column = 1 + file * 2 + 1;
final SideBySideLine line = pl.lines.get(file);
createCommentEditor(row + 1, column, line.getLineNumber(), file);
return;
}
super.onOpenItem(item);
}
@Override
protected void bindDrafts(final List<PatchLineComment> drafts) {
int[] rows = new int[fileCnt];
for (final PatchLineComment c : drafts) {
final int side = fileFor(c);
if (side < 0 || fileCnt <= side) {
// We shouldn't have been given this draft; it doesn't display
// in our current UI layout.
//
continue;
}
int row = rows[side];
while (row < table.getRowCount()) {
if (getRowItem(row) instanceof SideBySideLineList) {
final SideBySideLineList pl = (SideBySideLineList) getRowItem(row);
final SideBySideLine line = pl.lines.get(side);
if (line != null && line.getLineNumber() >= c.getLine()) {
break;
}
}
row++;
}
row++;
boolean needInsert = true;
for (int cell = 0; cell < table.getCellCount(row); cell++) {
final Widget w = table.getWidget(row, cell);
if (w instanceof CommentEditorPanel
|| w instanceof ComplexDisclosurePanel) {
needInsert = false;
break;
}
}
if (needInsert) {
table.insertRow(row);
table.getCellFormatter().setStyleName(row, 0, S_ICON_CELL);
}
bindComment(row, 1 + side * 2 + 1, c, true);
rows[side] = row + 1;
}
}
public void display(final SideBySidePatchDetail detail) {
setPatchKey(detail.getPatch().getKey());
initVersions(detail.getFileCount());
setAccountInfoCache(detail.getAccounts());
fileCnt = detail.getFileCount();
maxLineNumber = detail.getLineCount();
List<SideBySideLine> prior = null;
// Generate the table in HTML, because its quicker than by DOM.
// This pass does not include the line comments; they need full
// GWT widgets and are relatively infrequent. We do them later.
//
final SafeHtmlBuilder nc = new SafeHtmlBuilder();
appendHeader(nc);
for (final List<SideBySideLine> pLine : detail.getLines()) {
if (skipped(prior, pLine) > 0) {
appendSkipLine(nc);
}
prior = pLine;
appendFileLine(nc, pLine);
}
if (skipped(prior, null) > 0) {
appendSkipLine(nc);
}
resetHtml(nc);
// Insert the comment widgets now that the table DOM has been
// parsed out of the HTML by the browser. We also bind each
// of the row item objects.
//
int row = 1;
prior = null;
for (final List<SideBySideLine> pLine : detail.getLines()) {
final int skipCnt = skipped(prior, pLine);
if (skipCnt > 0) {
bindSkipLine(row, skipCnt);
row++;
}
prior = pLine;
setRowItem(row, new SideBySideLineList(pLine));
final int lineRow = row;
for (int fileId = 0; fileId < fileCnt; fileId++) {
final SideBySideLine s = pLine.get(fileId);
if (s == null) {
continue;
}
final List<PatchLineComment> comments = s.getComments();
if (comments == null) {
continue;
}
int commentRow = lineRow + 1;
for (Iterator<PatchLineComment> ci = comments.iterator(); ci.hasNext();) {
final PatchLineComment c = ci.next();
boolean needInsert = true;
for (int cell = 0; cell < table.getCellCount(commentRow); cell++) {
final Widget w = table.getWidget(commentRow, cell);
if (w instanceof CommentEditorPanel
|| w instanceof ComplexDisclosurePanel) {
needInsert = false;
break;
}
}
if (needInsert) {
table.insertRow(commentRow);
table.getCellFormatter().setStyleName(commentRow, 0, S_ICON_CELL);
}
table.setWidget(commentRow, 1 + 2 * fileId, null);
bindComment(commentRow, 1 + 2 * fileId + 1, c, !ci.hasNext());
commentRow++;
}
row = Math.max(row, commentRow - 1);
}
row++;
}
final int skipCnt = skipped(prior, null);
if (skipCnt > 0) {
bindSkipLine(row, skipCnt);
row++;
}
}
private void appendHeader(final SafeHtmlBuilder m) {
final String width = (100 / fileCnt) + "%";
m.openTr();
m.openTd();
m.addStyleName(S_ICON_CELL);
m.addStyleName("FileColumnHeader");
m.nbsp();
m.closeTd();
if (fileCnt == 2) {
m.openTd();
m.addStyleName("FileColumnHeader");
m.addStyleName("LineNumber");
m.nbsp();
m.closeTd();
m.openTd();
m.setStyleName("FileColumnHeader");
m.setAttribute("width", width);
m.append(PatchUtil.C.patchHeaderOld());
m.closeTd();
} else {
for (int fileId = 0; fileId < fileCnt - 1; fileId++) {
m.openTd();
m.addStyleName("FileColumnHeader");
m.addStyleName("LineNumber");
m.nbsp();
m.closeTd();
m.openTd();
m.setStyleName("FileColumnHeader");
m.setAttribute("width", width);
m.append(PatchUtil.M.patchHeaderAncestor(fileId + 1));
m.closeTd();
}
}
m.openTd();
m.addStyleName("FileColumnHeader");
m.addStyleName("LineNumber");
m.nbsp();
m.closeTd();
m.openTd();
m.setStyleName("FileColumnHeader");
m.setAttribute("width", width);
m.append(PatchUtil.C.patchHeaderNew());
m.closeTd();
m.closeTr();
}
private int skipped(List<SideBySideLine> prior,
final List<SideBySideLine> pLine) {
int existCnt = 0;
int gapCnt = 0;
int lines = 0;
if (prior != null && pLine != null) {
for (int i = 0; i < fileCnt; i++) {
final SideBySideLine ps = prior.get(i);
final SideBySideLine cs = pLine.get(i);
if (ps != null && cs != null) {
existCnt++;
if (ps.getLineNumber() + 1 != cs.getLineNumber()) {
lines =
Math.max(lines, cs.getLineNumber() - ps.getLineNumber() - 1);
gapCnt++;
}
}
}
} else if (prior != null) {
for (int i = 0; i < fileCnt; i++) {
final SideBySideLine ps = prior.get(i);
if (ps != null) {
existCnt++;
if (ps.getLineNumber() < maxLineNumber) {
lines = Math.max(lines, maxLineNumber - ps.getLineNumber() - 1);
gapCnt++;
}
}
}
} else {
for (int i = 0; i < fileCnt; i++) {
final SideBySideLine cs = pLine.get(i);
if (cs != null) {
existCnt++;
if (1 != cs.getLineNumber()) {
lines = Math.max(lines, cs.getLineNumber() - 1);
gapCnt++;
}
}
}
}
return existCnt == gapCnt ? lines : 0;
}
private void appendSkipLine(final SafeHtmlBuilder m) {
m.openTr();
m.openTd();
m.setStyleName(S_ICON_CELL);
m.nbsp();
m.closeTd();
m.openTd();
m.setStyleName("SkipLine");
m.setAttribute("colspan", fileCnt * 2);
m.closeTd();
m.closeTr();
}
private void bindSkipLine(int row, final int skipCnt) {
final FlowPanel skipPanel = new FlowPanel();
skipPanel.add(new InlineLabel(PatchUtil.M.patchSkipRegion(skipCnt)));
table.setWidget(row, 1, skipPanel);
}
private void appendFileLine(final SafeHtmlBuilder m,
final List<SideBySideLine> line) {
m.openTr();
m.setAttribute("valign", "top");
m.openTd();
m.setStyleName(S_ICON_CELL);
m.nbsp();
m.closeTd();
for (int fileId = 0; fileId < fileCnt; fileId++) {
final SideBySideLine s = line.get(fileId);
if (s != null) {
m.openTd();
m.setStyleName("LineNumber");
m.append(s.getLineNumber());
m.closeTd();
m.openTd();
m.addStyleName("FileLine");
m.addStyleName("FileLine-" + s.getType().name());
if (!"".equals(s.getText())) {
boolean showWhitespaceErrors = false;
if (fileId == fileCnt - 1
&& s.getType() == SideBySideLine.Type.INSERT) {
// Only show whitespace errors in the last column, and
// only if the line is introduced here.
//
showWhitespaceErrors = true;
}
m.append(PatchUtil.lineToSafeHtml(s.getText(),
PatchUtil.DEFAULT_LINE_LENGTH, showWhitespaceErrors));
} else {
m.nbsp();
}
m.closeTd();
} else {
m.openTd();
m.setStyleName("LineNumber");
m.nbsp();
m.closeTd();
m.openTd();
m.addStyleName("FileLine");
m.addStyleName("FileLineNone");
m.nbsp();
m.closeTd();
}
}
m.closeTr();
}
private static class SideBySideLineList {
final List<SideBySideLine> lines;
SideBySideLineList(final List<SideBySideLine> a) {
lines = a;
}
}
}