|  | // 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 static com.google.gerrit.client.FormatUtil.relativeFormat; | 
|  | import static com.google.gerrit.client.FormatUtil.shortFormat; | 
|  | import static java.util.stream.Collectors.toList; | 
|  |  | 
|  | import com.google.gerrit.client.Gerrit; | 
|  | import com.google.gerrit.client.info.AccountInfo; | 
|  | import com.google.gerrit.client.info.ChangeInfo; | 
|  | import com.google.gerrit.client.info.ChangeInfo.LabelInfo; | 
|  | import com.google.gerrit.client.rpc.Natives; | 
|  | import com.google.gerrit.client.ui.AccountLinkPanel; | 
|  | import com.google.gerrit.client.ui.BranchLink; | 
|  | import com.google.gerrit.client.ui.ChangeLink; | 
|  | import com.google.gerrit.client.ui.NavigationTable; | 
|  | import com.google.gerrit.client.ui.NeedsSignInKeyCommand; | 
|  | import com.google.gerrit.client.ui.ProjectLink; | 
|  | import com.google.gerrit.common.PageLinks; | 
|  | import com.google.gerrit.extensions.client.GeneralPreferencesInfo.ReviewCategoryStrategy; | 
|  | import com.google.gerrit.extensions.client.ListChangesOption; | 
|  | import com.google.gerrit.reviewdb.client.Change; | 
|  | import com.google.gwt.dom.client.Element; | 
|  | import com.google.gwt.event.dom.client.ClickEvent; | 
|  | import com.google.gwt.event.dom.client.ClickHandler; | 
|  | import com.google.gwt.event.dom.client.KeyPressEvent; | 
|  | import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter; | 
|  | import com.google.gwt.user.client.ui.FlowPanel; | 
|  | import com.google.gwt.user.client.ui.HTMLTable.Cell; | 
|  | import com.google.gwt.user.client.ui.HTMLTable.CellFormatter; | 
|  | import com.google.gwt.user.client.ui.Image; | 
|  | import com.google.gwt.user.client.ui.InlineLabel; | 
|  | import com.google.gwt.user.client.ui.SimplePanel; | 
|  | import com.google.gwt.user.client.ui.UIObject; | 
|  | import com.google.gwt.user.client.ui.Widget; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Arrays; | 
|  | import java.util.Collections; | 
|  | import java.util.EnumSet; | 
|  | import java.util.List; | 
|  | import java.util.Objects; | 
|  | import java.util.Set; | 
|  |  | 
|  | public class ChangeTable extends NavigationTable<ChangeInfo> { | 
|  | // If changing default options, also update in | 
|  | // ChangeIT#defaultSearchDoesNotTouchDatabase(). | 
|  | static final Set<ListChangesOption> OPTIONS = | 
|  | Collections.unmodifiableSet( | 
|  | EnumSet.of(ListChangesOption.LABELS, ListChangesOption.DETAILED_ACCOUNTS)); | 
|  |  | 
|  | private static final int C_STAR = 1; | 
|  | private static final int C_ID = 2; | 
|  | private static final int C_SUBJECT = 3; | 
|  | private static final int C_STATUS = 4; | 
|  | private static final int C_OWNER = 5; | 
|  | private static final int C_ASSIGNEE = 6; | 
|  | private static final int C_PROJECT = 7; | 
|  | private static final int C_BRANCH = 8; | 
|  | private static final int C_LAST_UPDATE = 9; | 
|  | private static final int C_SIZE = 10; | 
|  | private static final int BASE_COLUMNS = 11; | 
|  |  | 
|  | private final List<Section> sections; | 
|  | private int columns; | 
|  | private final boolean showAssignee; | 
|  | private final boolean showLegacyId; | 
|  | private List<String> labelNames; | 
|  |  | 
|  | public ChangeTable() { | 
|  | super(Util.C.changeItemHelp()); | 
|  | columns = BASE_COLUMNS; | 
|  | labelNames = Collections.emptyList(); | 
|  | showAssignee = Gerrit.info().change().showAssigneeInChangesTable(); | 
|  | showLegacyId = Gerrit.getUserPreferences().legacycidInChangeTable(); | 
|  |  | 
|  | if (Gerrit.isSignedIn()) { | 
|  | keysAction.add(new StarKeyCommand(0, 's', Util.C.changeTableStar())); | 
|  | } | 
|  |  | 
|  | sections = new ArrayList<>(); | 
|  | table.setText(0, C_STAR, ""); | 
|  | table.setText(0, C_ID, Util.C.changeTableColumnID()); | 
|  | table.setText(0, C_SUBJECT, Util.C.changeTableColumnSubject()); | 
|  | table.setText(0, C_STATUS, Util.C.changeTableColumnStatus()); | 
|  | table.setText(0, C_OWNER, Util.C.changeTableColumnOwner()); | 
|  | table.setText(0, C_ASSIGNEE, Util.C.changeTableColumnAssignee()); | 
|  | table.setText(0, C_PROJECT, Util.C.changeTableColumnProject()); | 
|  | table.setText(0, C_BRANCH, Util.C.changeTableColumnBranch()); | 
|  | table.setText(0, C_LAST_UPDATE, Util.C.changeTableColumnLastUpdate()); | 
|  | table.setText(0, C_SIZE, Util.C.changeTableColumnSize()); | 
|  |  | 
|  | final FlexCellFormatter fmt = table.getFlexCellFormatter(); | 
|  | fmt.addStyleName(0, C_STAR, Gerrit.RESOURCES.css().iconHeader()); | 
|  | for (int i = C_ID; i < columns; i++) { | 
|  | fmt.addStyleName(0, i, Gerrit.RESOURCES.css().dataHeader()); | 
|  | } | 
|  | if (!showLegacyId) { | 
|  | fmt.addStyleName(0, C_ID, Gerrit.RESOURCES.css().dataHeaderHidden()); | 
|  | } | 
|  | if (!showAssignee) { | 
|  | fmt.addStyleName(0, C_ASSIGNEE, Gerrit.RESOURCES.css().dataHeaderHidden()); | 
|  | } | 
|  |  | 
|  | table.addClickHandler( | 
|  | new ClickHandler() { | 
|  | @Override | 
|  | public void onClick(ClickEvent event) { | 
|  | final Cell cell = table.getCellForEvent(event); | 
|  | if (cell == null) { | 
|  | return; | 
|  | } | 
|  | if (cell.getCellIndex() == C_STAR) { | 
|  | // Don't do anything (handled by star itself). | 
|  | } else if (cell.getCellIndex() == C_STATUS) { | 
|  | // Don't do anything. | 
|  | } else if (cell.getCellIndex() == C_OWNER) { | 
|  | // Don't do anything. | 
|  | } else if (getRowItem(cell.getRowIndex()) != null) { | 
|  | movePointerTo(cell.getRowIndex()); | 
|  | } | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected Object getRowItemKey(ChangeInfo item) { | 
|  | return item.legacyId(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected void onOpenRow(int row) { | 
|  | final ChangeInfo c = getRowItem(row); | 
|  | Gerrit.display(PageLinks.toChange(c.projectNameKey(), c.legacyId())); | 
|  | } | 
|  |  | 
|  | private void insertNoneRow(int row) { | 
|  | insertRow(row); | 
|  | table.setText(row, 0, Util.C.changeTableNone()); | 
|  | final FlexCellFormatter fmt = table.getFlexCellFormatter(); | 
|  | fmt.setColSpan(row, 0, columns); | 
|  | fmt.setStyleName(row, 0, Gerrit.RESOURCES.css().emptySection()); | 
|  | } | 
|  |  | 
|  | private void insertChangeRow(int row) { | 
|  | insertRow(row); | 
|  | applyDataRowStyle(row); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected void applyDataRowStyle(int row) { | 
|  | super.applyDataRowStyle(row); | 
|  | final CellFormatter fmt = table.getCellFormatter(); | 
|  | fmt.addStyleName(row, C_STAR, Gerrit.RESOURCES.css().iconCell()); | 
|  | for (int i = C_ID; i < columns; i++) { | 
|  | fmt.addStyleName(row, i, Gerrit.RESOURCES.css().dataCell()); | 
|  | } | 
|  | if (!showLegacyId) { | 
|  | fmt.addStyleName(row, C_ID, Gerrit.RESOURCES.css().dataCellHidden()); | 
|  | } | 
|  | fmt.addStyleName(row, C_SUBJECT, Gerrit.RESOURCES.css().cSUBJECT()); | 
|  | fmt.addStyleName(row, C_STATUS, Gerrit.RESOURCES.css().cSTATUS()); | 
|  | fmt.addStyleName(row, C_OWNER, Gerrit.RESOURCES.css().cOWNER()); | 
|  | fmt.addStyleName( | 
|  | row, | 
|  | C_ASSIGNEE, | 
|  | showAssignee | 
|  | ? Gerrit.RESOURCES.css().cASSIGNEE() | 
|  | : Gerrit.RESOURCES.css().dataCellHidden()); | 
|  | fmt.addStyleName(row, C_LAST_UPDATE, Gerrit.RESOURCES.css().cLastUpdate()); | 
|  | fmt.addStyleName(row, C_SIZE, Gerrit.RESOURCES.css().cSIZE()); | 
|  |  | 
|  | for (int i = C_SIZE + 1; i < columns; i++) { | 
|  | fmt.addStyleName(row, i, Gerrit.RESOURCES.css().cAPPROVAL()); | 
|  | } | 
|  | } | 
|  |  | 
|  | public void updateColumnsForLabels(ChangeList... lists) { | 
|  | labelNames = | 
|  | Arrays.stream(lists) | 
|  | .flatMap(l -> Natives.asList(l).stream()) | 
|  | .flatMap(c -> c.labels().stream()) | 
|  | .distinct() | 
|  | .sorted() | 
|  | .collect(toList()); | 
|  |  | 
|  | int baseColumns = BASE_COLUMNS; | 
|  | if (baseColumns + labelNames.size() < columns) { | 
|  | int n = columns - (baseColumns + labelNames.size()); | 
|  | for (int row = 0; row < table.getRowCount(); row++) { | 
|  | table.removeCells(row, columns, n); | 
|  | } | 
|  | } | 
|  | columns = baseColumns + labelNames.size(); | 
|  |  | 
|  | FlexCellFormatter fmt = table.getFlexCellFormatter(); | 
|  | for (int i = 0; i < labelNames.size(); i++) { | 
|  | String name = labelNames.get(i); | 
|  | int col = baseColumns + i; | 
|  |  | 
|  | String abbrev = getAbbreviation(name, "-"); | 
|  | table.setText(0, col, abbrev); | 
|  | table.getCellFormatter().getElement(0, col).setTitle(name); | 
|  | fmt.addStyleName(0, col, Gerrit.RESOURCES.css().dataHeader()); | 
|  | } | 
|  |  | 
|  | for (Section s : sections) { | 
|  | if (s.titleRow >= 0) { | 
|  | fmt.setColSpan(s.titleRow, 0, columns); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void populateChangeRow(int row, ChangeInfo c, boolean highlightUnreviewed) { | 
|  | CellFormatter fmt = table.getCellFormatter(); | 
|  | if (Gerrit.isSignedIn()) { | 
|  | table.setWidget(row, C_STAR, StarredChanges.createIcon(c.legacyId(), c.starred())); | 
|  | } | 
|  | table.setWidget(row, C_ID, new TableChangeLink(String.valueOf(c.legacyId()), c)); | 
|  |  | 
|  | String subject = Util.cropSubject(c.subject()); | 
|  | table.setWidget(row, C_SUBJECT, new TableChangeLink(subject, c)); | 
|  |  | 
|  | Change.Status status = c.status(); | 
|  | if (status != Change.Status.NEW) { | 
|  | table.setText( | 
|  | row, | 
|  | C_STATUS, | 
|  | Util.toLongString(status) + (c.isPrivate() ? (" " + Util.C.isPrivate()) : "")); | 
|  | } else if (c.isWorkInProgress()) { | 
|  | table.setText( | 
|  | row, | 
|  | C_STATUS, | 
|  | Util.C.workInProgress() + (c.isPrivate() ? (" " + Util.C.isPrivate()) : "")); | 
|  | } else if (!c.mergeable()) { | 
|  | table.setText( | 
|  | row, | 
|  | C_STATUS, | 
|  | Util.C.changeTableNotMergeable() + (c.isPrivate() ? (" " + Util.C.isPrivate()) : "")); | 
|  | } else if (c.isPrivate()) { | 
|  | table.setText(row, C_STATUS, Util.C.isPrivate()); | 
|  | } | 
|  |  | 
|  | if (c.owner() != null) { | 
|  | table.setWidget(row, C_OWNER, AccountLinkPanel.withStatus(c.owner(), status)); | 
|  | } else { | 
|  | table.setText(row, C_OWNER, ""); | 
|  | } | 
|  |  | 
|  | if (showAssignee) { | 
|  | if (c.assignee() != null) { | 
|  | table.setWidget(row, C_ASSIGNEE, AccountLinkPanel.forAssignee(c.assignee())); | 
|  | if (Gerrit.getUserPreferences().highlightAssigneeInChangeTable() | 
|  | && Objects.equals(c.assignee()._accountId(), Gerrit.getUserAccount()._accountId())) { | 
|  | table.getRowFormatter().addStyleName(row, Gerrit.RESOURCES.css().cASSIGNEDTOME()); | 
|  | } | 
|  | } else { | 
|  | table.setText(row, C_ASSIGNEE, ""); | 
|  | } | 
|  | } | 
|  |  | 
|  | table.setWidget(row, C_PROJECT, new ProjectLink(c.projectNameKey())); | 
|  | table.setWidget( | 
|  | row, C_BRANCH, new BranchLink(c.projectNameKey(), c.status(), c.branch(), c.topic())); | 
|  | if (Gerrit.getUserPreferences().relativeDateInChangeTable()) { | 
|  | table.setText(row, C_LAST_UPDATE, relativeFormat(c.updated())); | 
|  | } else { | 
|  | table.setText(row, C_LAST_UPDATE, shortFormat(c.updated())); | 
|  | } | 
|  |  | 
|  | int col = C_SIZE; | 
|  | if (!Gerrit.getUserPreferences().sizeBarInChangeTable()) { | 
|  | table.setText(row, col, Util.M.insertionsAndDeletions(c.insertions(), c.deletions())); | 
|  | } else { | 
|  | table.setWidget(row, col, getSizeWidget(c)); | 
|  | fmt.getElement(row, col) | 
|  | .setTitle(Util.M.insertionsAndDeletions(c.insertions(), c.deletions())); | 
|  | } | 
|  | col++; | 
|  |  | 
|  | for (int idx = 0; idx < labelNames.size(); idx++, col++) { | 
|  | String name = labelNames.get(idx); | 
|  |  | 
|  | LabelInfo label = c.label(name); | 
|  | if (label == null) { | 
|  | fmt.getElement(row, col).setTitle(Gerrit.C.labelNotApplicable()); | 
|  | fmt.addStyleName(row, col, Gerrit.RESOURCES.css().labelNotApplicable()); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | String user; | 
|  | String info; | 
|  | ReviewCategoryStrategy reviewCategoryStrategy = | 
|  | Gerrit.getUserPreferences().reviewCategoryStrategy(); | 
|  | if (label.rejected() != null) { | 
|  | user = label.rejected().name(); | 
|  | info = getReviewCategoryDisplayInfo(reviewCategoryStrategy, label.rejected()); | 
|  | if (info != null) { | 
|  | FlowPanel panel = new FlowPanel(); | 
|  | panel.add(new Image(Gerrit.RESOURCES.redNot())); | 
|  | panel.add(new InlineLabel(info)); | 
|  | table.setWidget(row, col, panel); | 
|  | } else { | 
|  | table.setWidget(row, col, new Image(Gerrit.RESOURCES.redNot())); | 
|  | } | 
|  | } else if (label.approved() != null) { | 
|  | user = label.approved().name(); | 
|  | info = getReviewCategoryDisplayInfo(reviewCategoryStrategy, label.approved()); | 
|  | if (info != null) { | 
|  | FlowPanel panel = new FlowPanel(); | 
|  | panel.add(new Image(Gerrit.RESOURCES.greenCheck())); | 
|  | panel.add(new InlineLabel(info)); | 
|  | table.setWidget(row, col, panel); | 
|  | } else { | 
|  | table.setWidget(row, col, new Image(Gerrit.RESOURCES.greenCheck())); | 
|  | } | 
|  | } else if (label.disliked() != null) { | 
|  | user = label.disliked().name(); | 
|  | info = getReviewCategoryDisplayInfo(reviewCategoryStrategy, label.disliked()); | 
|  | String vstr = String.valueOf(label._value()); | 
|  | if (info != null) { | 
|  | vstr = vstr + " " + info; | 
|  | } | 
|  | fmt.addStyleName(row, col, Gerrit.RESOURCES.css().negscore()); | 
|  | table.setText(row, col, vstr); | 
|  | } else if (label.recommended() != null) { | 
|  | user = label.recommended().name(); | 
|  | info = getReviewCategoryDisplayInfo(reviewCategoryStrategy, label.recommended()); | 
|  | String vstr = "+" + label._value(); | 
|  | if (info != null) { | 
|  | vstr = vstr + " " + info; | 
|  | } | 
|  | fmt.addStyleName(row, col, Gerrit.RESOURCES.css().posscore()); | 
|  | table.setText(row, col, vstr); | 
|  | } else { | 
|  | table.clearCell(row, col); | 
|  | continue; | 
|  | } | 
|  | fmt.addStyleName(row, col, Gerrit.RESOURCES.css().singleLine()); | 
|  |  | 
|  | if (user != null) { | 
|  | // Some web browsers ignore the embedded newline; some like it; | 
|  | // so we include a space before the newline to accommodate both. | 
|  | fmt.getElement(row, col).setTitle(name + " \nby " + user); | 
|  | } | 
|  | } | 
|  |  | 
|  | boolean needHighlight = false; | 
|  | if (highlightUnreviewed && !c.reviewed()) { | 
|  | needHighlight = true; | 
|  | } | 
|  | final Element tr = fmt.getElement(row, 0).getParentElement(); | 
|  | UIObject.setStyleName(tr, Gerrit.RESOURCES.css().needsReview(), needHighlight); | 
|  |  | 
|  | setRowItem(row, c); | 
|  | } | 
|  |  | 
|  | private static String getReviewCategoryDisplayInfo( | 
|  | ReviewCategoryStrategy reviewCategoryStrategy, AccountInfo accountInfo) { | 
|  | switch (reviewCategoryStrategy) { | 
|  | case NAME: | 
|  | return accountInfo.name(); | 
|  | case EMAIL: | 
|  | return accountInfo.email(); | 
|  | case USERNAME: | 
|  | return accountInfo.username(); | 
|  | case ABBREV: | 
|  | return getAbbreviation(accountInfo.name(), " "); | 
|  | case NONE: | 
|  | default: | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | private static String getAbbreviation(String name, String token) { | 
|  | StringBuilder abbrev = new StringBuilder(); | 
|  | if (name != null) { | 
|  | for (String t : name.split(token)) { | 
|  | abbrev.append(t.substring(0, 1).toUpperCase()); | 
|  | } | 
|  | } | 
|  | return abbrev.toString(); | 
|  | } | 
|  |  | 
|  | private static Widget getSizeWidget(ChangeInfo c) { | 
|  | int largeChangeSize = Gerrit.info().change().largeChange(); | 
|  | int changedLines = c.insertions() + c.deletions(); | 
|  | int p = 100; | 
|  | if (changedLines < largeChangeSize) { | 
|  | p = changedLines * 100 / largeChangeSize; | 
|  | } | 
|  |  | 
|  | int width = Math.max(2, 70 * p / 100); | 
|  | int red = p >= 50 ? 255 : (int) Math.round((p) * 5.12); | 
|  | int green = p <= 50 ? 255 : (int) Math.round(256 - (p - 50) * 5.12); | 
|  | String bg = "#" + toHex(red) + toHex(green) + "00"; | 
|  |  | 
|  | SimplePanel panel = new SimplePanel(); | 
|  | panel.setStyleName(Gerrit.RESOURCES.css().changeSize()); | 
|  | panel.setWidth(width + "px"); | 
|  | panel.getElement().getStyle().setBackgroundColor(bg); | 
|  | return panel; | 
|  | } | 
|  |  | 
|  | private static String toHex(int i) { | 
|  | String hex = Integer.toHexString(i); | 
|  | return hex.length() == 1 ? "0" + hex : hex; | 
|  | } | 
|  |  | 
|  | public void addSection(Section s) { | 
|  | assert s.parent == null; | 
|  |  | 
|  | s.parent = this; | 
|  | s.titleRow = table.getRowCount(); | 
|  | if (s.displayTitle()) { | 
|  | final FlexCellFormatter fmt = table.getFlexCellFormatter(); | 
|  | fmt.setColSpan(s.titleRow, 0, columns); | 
|  | fmt.addStyleName(s.titleRow, 0, Gerrit.RESOURCES.css().sectionHeader()); | 
|  | } else { | 
|  | s.titleRow = -1; | 
|  | } | 
|  |  | 
|  | s.dataBegin = table.getRowCount(); | 
|  | insertNoneRow(s.dataBegin); | 
|  | sections.add(s); | 
|  | } | 
|  |  | 
|  | private int insertRow(int beforeRow) { | 
|  | for (Section s : sections) { | 
|  | if (beforeRow <= s.titleRow) { | 
|  | s.titleRow++; | 
|  | } | 
|  | if (beforeRow < s.dataBegin) { | 
|  | s.dataBegin++; | 
|  | } | 
|  | } | 
|  | return table.insertRow(beforeRow); | 
|  | } | 
|  |  | 
|  | private void removeRow(int row) { | 
|  | for (Section s : sections) { | 
|  | if (row < s.titleRow) { | 
|  | s.titleRow--; | 
|  | } | 
|  | if (row < s.dataBegin) { | 
|  | s.dataBegin--; | 
|  | } | 
|  | } | 
|  | table.removeRow(row); | 
|  | } | 
|  |  | 
|  | public class StarKeyCommand extends NeedsSignInKeyCommand { | 
|  | public StarKeyCommand(int mask, char key, String help) { | 
|  | super(mask, key, help); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onKeyPress(KeyPressEvent event) { | 
|  | int row = getCurrentRow(); | 
|  | ChangeInfo c = getRowItem(row); | 
|  | if (c != null && Gerrit.isSignedIn()) { | 
|  | ((StarredChanges.Icon) table.getWidget(row, C_STAR)).toggleStar(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private final class TableChangeLink extends ChangeLink { | 
|  | private TableChangeLink(String text, ChangeInfo c) { | 
|  | super(c.projectNameKey(), c.legacyId(), text); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void go() { | 
|  | movePointerTo(cid); | 
|  | super.go(); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class Section { | 
|  | ChangeTable parent; | 
|  | String titleText; | 
|  | Widget titleWidget; | 
|  | int titleRow = -1; | 
|  | int dataBegin; | 
|  | int rows; | 
|  | private boolean highlightUnreviewed; | 
|  |  | 
|  | public void setHighlightUnreviewed(boolean value) { | 
|  | this.highlightUnreviewed = value; | 
|  | } | 
|  |  | 
|  | public void setTitleText(String text) { | 
|  | titleText = text; | 
|  | titleWidget = null; | 
|  | if (titleRow >= 0) { | 
|  | parent.table.setText(titleRow, 0, titleText); | 
|  | } | 
|  | } | 
|  |  | 
|  | public void setTitleWidget(Widget title) { | 
|  | titleWidget = title; | 
|  | titleText = null; | 
|  | if (titleRow >= 0) { | 
|  | parent.table.setWidget(titleRow, 0, title); | 
|  | } | 
|  | } | 
|  |  | 
|  | public boolean displayTitle() { | 
|  | if (titleText != null) { | 
|  | setTitleText(titleText); | 
|  | return true; | 
|  | } else if (titleWidget != null) { | 
|  | setTitleWidget(titleWidget); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public void display(ChangeList changeList) { | 
|  | final int sz = changeList != null ? changeList.length() : 0; | 
|  | final boolean hadData = rows > 0; | 
|  |  | 
|  | if (hadData) { | 
|  | while (sz < rows) { | 
|  | parent.removeRow(dataBegin); | 
|  | rows--; | 
|  | } | 
|  | } else { | 
|  | parent.removeRow(dataBegin); | 
|  | } | 
|  |  | 
|  | if (sz == 0) { | 
|  | parent.insertNoneRow(dataBegin); | 
|  | return; | 
|  | } | 
|  |  | 
|  | while (rows < sz) { | 
|  | parent.insertChangeRow(dataBegin + rows); | 
|  | rows++; | 
|  | } | 
|  | for (int i = 0; i < sz; i++) { | 
|  | parent.populateChangeRow(dataBegin + i, changeList.get(i), highlightUnreviewed); | 
|  | } | 
|  | } | 
|  | } | 
|  | } |