// 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.shortFormat; import com.google.gerrit.client.Gerrit; 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.common.data.AccountInfoCache; import com.google.gerrit.common.data.ChangeInfo; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; 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.HTMLTable.Cell; import com.google.gwt.user.client.ui.HTMLTable.CellFormatter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class ChangeTable extends NavigationTable<ChangeInfo> { private static final int C_STAR = 1; private static final int C_SUBJECT = 2; private static final int C_OWNER = 3; private static final int C_PROJECT = 4; private static final int C_BRANCH = 5; private static final int C_LAST_UPDATE = 6; private static final int COLUMNS = 7; private final List<Section> sections; private AccountInfoCache accountCache = AccountInfoCache.empty(); public ChangeTable() { super(Util.C.changeItemHelp()); if (Gerrit.isSignedIn()) { keysAction.add(new StarKeyCommand(0, 's', Util.C.changeTableStar())); } sections = new ArrayList<>(); table.setText(0, C_STAR, ""); table.setText(0, C_SUBJECT, Util.C.changeTableColumnSubject()); table.setText(0, C_OWNER, Util.C.changeTableColumnOwner()); 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()); final FlexCellFormatter fmt = table.getFlexCellFormatter(); fmt.addStyleName(0, C_STAR, Gerrit.RESOURCES.css().iconHeader()); for (int i = C_SUBJECT; i < COLUMNS; i++) { fmt.addStyleName(0, i, Gerrit.RESOURCES.css().dataHeader()); } table.addClickHandler(new ClickHandler() { @Override public void onClick(final 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_OWNER) { // Don't do anything. } else if (getRowItem(cell.getRowIndex()) != null) { movePointerTo(cell.getRowIndex()); } } }); } protected void onStarClick(final int row) { final ChangeInfo c = getRowItem(row); if (c != null && Gerrit.isSignedIn()) { ((StarredChanges.Icon) table.getWidget(row, C_STAR)).toggleStar(); } } @Override protected Object getRowItemKey(final ChangeInfo item) { return item.getId(); } @Override protected void onOpenRow(final int row) { final ChangeInfo c = getRowItem(row); Gerrit.display(PageLinks.toChange(c), new ChangeScreen(c)); } private void insertNoneRow(final 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(final int row) { insertRow(row); applyDataRowStyle(row); } @Override protected void applyDataRowStyle(final int row) { super.applyDataRowStyle(row); final CellFormatter fmt = table.getCellFormatter(); fmt.addStyleName(row, C_STAR, Gerrit.RESOURCES.css().iconCell()); for (int i = C_SUBJECT; i < COLUMNS; i++) { fmt.addStyleName(row, i, Gerrit.RESOURCES.css().dataCell()); } fmt.addStyleName(row, C_SUBJECT, Gerrit.RESOURCES.css().cSUBJECT()); fmt.addStyleName(row, C_OWNER, Gerrit.RESOURCES.css().cOWNER()); fmt.addStyleName(row, C_LAST_UPDATE, Gerrit.RESOURCES.css().cLastUpdate()); } private void populateChangeRow(final int row, final ChangeInfo c, final ChangeRowFormatter changeRowFormatter) { ChangeCache cache = ChangeCache.get(c.getId()); cache.getChangeInfoCache().set(c); table.setWidget(row, C_ARROW, null); if (Gerrit.isSignedIn()) { table.setWidget(row, C_STAR, StarredChanges.createIcon(c.getId(), c.isStarred())); } String s = Util.cropSubject(c.getSubject()); if (c.getStatus() != null && c.getStatus() != Change.Status.NEW) { s += " (" + c.getStatus().name() + ")"; } if (changeRowFormatter != null) { removeChangeStyle(row, changeRowFormatter); final String rowStyle = changeRowFormatter.getRowStyle(c); if (rowStyle != null) { table.getRowFormatter().addStyleName(row, rowStyle); } s = changeRowFormatter.getDisplayText(c, s); } table.setWidget(row, C_SUBJECT, new TableChangeLink(s, c)); table.setWidget(row, C_OWNER, link(c.getOwner())); table.setWidget(row, C_PROJECT, new ProjectLink(c.getProject().getKey())); table.setWidget(row, C_BRANCH, new BranchLink(c.getProject().getKey(), c .getStatus(), c.getBranch(), c.getTopic())); table.setText(row, C_LAST_UPDATE, shortFormat(c.getLastUpdatedOn())); setRowItem(row, c); } private void removeChangeStyle(int row, final ChangeRowFormatter changeRowFormatter) { final ChangeInfo oldChange = getRowItem(row); if (oldChange == null) { return; } final String oldRowStyle = changeRowFormatter.getRowStyle(oldChange); if (oldRowStyle != null) { table.getRowFormatter().removeStyleName(row, oldRowStyle); } } private AccountLinkPanel link(final Account.Id id) { return AccountLinkPanel.link(accountCache, id); } public void addSection(final Section s) { assert s.parent == null; if (s.titleText != null) { s.titleRow = table.getRowCount(); table.setText(s.titleRow, 0, s.titleText); 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.parent = this; s.dataBegin = table.getRowCount(); insertNoneRow(s.dataBegin); sections.add(s); } public void setAccountInfoCache(final AccountInfoCache aic) { assert aic != null; accountCache = aic; } private int insertRow(final int beforeRow) { for (final Section s : sections) { if (beforeRow <= s.titleRow) { s.titleRow++; } if (beforeRow < s.dataBegin) { s.dataBegin++; } } return table.insertRow(beforeRow); } private void removeRow(final int row) { for (final 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(final KeyPressEvent event) { onStarClick(getCurrentRow()); } } private final class TableChangeLink extends ChangeLink { private TableChangeLink(final String text, final ChangeInfo c) { super(text, c); } @Override public void go() { movePointerTo(cid); super.go(); } } public static class Section { String titleText; ChangeTable parent; final Account.Id ownerId; int titleRow = -1; int dataBegin; int rows; private ChangeRowFormatter changeRowFormatter; public Section() { this(null, null); } public Section(final String titleText) { this(titleText, null); } public Section(final String titleText, final Account.Id owner) { setTitleText(titleText); ownerId = owner; } public void setTitleText(final String text) { titleText = text; if (titleRow >= 0) { parent.table.setText(titleRow, 0, titleText); } } public void setChangeRowFormatter(final ChangeRowFormatter changeRowFormatter) { this.changeRowFormatter = changeRowFormatter; } public void display(final List<ChangeInfo> changeList) { final int sz = changeList != null ? changeList.size() : 0; final boolean hadData = rows > 0; if (hadData) { while (sz < rows) { parent.removeRow(dataBegin); rows--; } } if (sz == 0) { if (hadData) { parent.insertNoneRow(dataBegin); } } else { Set<Change.Id> cids = new HashSet<>(); if (!hadData) { parent.removeRow(dataBegin); } while (rows < sz) { parent.insertChangeRow(dataBegin + rows); rows++; } for (int i = 0; i < sz; i++) { ChangeInfo c = changeList.get(i); parent.populateChangeRow(dataBegin + i, c, changeRowFormatter); cids.add(c.getId()); } } } } public static interface ChangeRowFormatter { /** * Returns the name of the CSS style that should be applied to the change * row. * * @param c the change for which the styling should be returned * @return the name of the CSS style that should be applied to the change * row */ String getRowStyle(ChangeInfo c); /** * Returns the text that should be displayed for the change. * * @param c the change for which the display text should be returned * @param displayText the current display text * @return the new display text */ String getDisplayText(ChangeInfo c, String displayText); } }