/* * CommitListTable.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.studio.client.workbench.views.vcs.dialog; import com.google.gwt.cell.client.AbstractSafeHtmlCell; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.text.shared.SafeHtmlRenderer; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.TextColumn; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SingleSelectionModel; import org.rstudio.core.client.SafeHtmlUtil; import org.rstudio.core.client.StringUtil; import org.rstudio.core.client.js.JsUtil; import org.rstudio.core.client.theme.RStudioCellTableStyle; import org.rstudio.core.client.widget.MultiSelectCellTable; import org.rstudio.studio.client.workbench.views.vcs.dialog.HistoryPanel.Styles; import org.rstudio.studio.client.workbench.views.vcs.dialog.HistoryPresenter.CommitListDisplay; import org.rstudio.studio.client.workbench.views.vcs.dialog.graph.GraphLine; import org.rstudio.studio.client.workbench.views.vcs.dialog.graph.GraphTheme; import java.util.List; public class CommitListTable extends MultiSelectCellTable<CommitInfo> implements CommitListDisplay { interface Resources extends CellTable.Resources { @Override @Source({RStudioCellTableStyle.RSTUDIO_DEFAULT_CSS, "CommitListTableCellTableStyle.css"}) Style cellTableStyle(); } interface CommitListTableCellTableStyle extends CellTable.Style { } private static class CommitColumn extends Column<CommitInfo, CommitInfo> { private static class RenderCell extends AbstractSafeHtmlCell<CommitInfo> { private RenderCell(SafeHtmlRenderer<CommitInfo> commitInfoSafeHtmlRenderer, String... consumedEvents) { super(commitInfoSafeHtmlRenderer, consumedEvents); } @Override protected void render(Context context, SafeHtml data, SafeHtmlBuilder sb) { if (data != null) sb.append(data); } } private CommitColumn(SafeHtmlRenderer<CommitInfo> renderer) { super(new RenderCell(renderer)); } @Override public CommitInfo getValue(CommitInfo object) { return object; } } @SuppressWarnings("unused") private class GraphAndSubjectRenderer implements SafeHtmlRenderer<CommitInfo> { private GraphAndSubjectRenderer(GraphTheme theme) { graphRenderer_ = new GraphRenderer(theme); subjectRenderer_ = new SubjectRenderer(); } @Override public SafeHtml render(CommitInfo object) { return SafeHtmlUtil.concat(graphRenderer_.render(object), subjectRenderer_.render(object)); } @Override public void render(CommitInfo object, SafeHtmlBuilder builder) { builder.append(render(object)); } private final GraphRenderer graphRenderer_; private final SubjectRenderer subjectRenderer_; } private class GraphRenderer implements SafeHtmlRenderer<CommitInfo> { public GraphRenderer(GraphTheme theme) { theme_ = theme; } @Override public SafeHtml render(CommitInfo object) { if (lastGraphImg_ != null && object.getGraph().equals(lastGraph_)) return lastGraphImg_; lastGraph_ = object.getGraph(); if (object.getGraph().length() == 0) return lastGraphImg_ = SafeHtmlUtil.createEmpty(); return lastGraphImg_ = new GraphLine(object.getGraph()).render(theme_); } @Override public void render(CommitInfo object, SafeHtmlBuilder builder) { builder.append(render(object)); } private final GraphTheme theme_; private String lastGraph_ = ""; private SafeHtml lastGraphImg_; } private class SubjectRenderer implements SafeHtmlRenderer<CommitInfo> { @Override public SafeHtml render(CommitInfo commit) { SafeHtmlBuilder builder = new SafeHtmlBuilder(); for (String ref : JsUtil.asIterable(commit.getRefs())) { String style = styles_.ref(); if (ref.startsWith("refs/heads/")) { ref = ref.substring("refs/heads/".length()); style += " " + styles_.branch(); } else if (ref.startsWith("refs/remotes/")) { ref = ref.substring("refs/remotes/".length()); style += " " + styles_.remote(); } else if (ref.equals("HEAD")) { style += " " + styles_.head(); } SafeHtmlUtil.appendSpan(builder, style, ref); } for (String tag : JsUtil.asIterable(commit.getTags())) { if (tag.startsWith("refs/tags/")) tag = tag.substring("refs/tags/".length()); SafeHtmlUtil.appendSpan(builder, styles_.tag(), tag); } builder.appendEscaped(commit.getSubject()); return builder.toSafeHtml(); } @Override public void render(CommitInfo object, SafeHtmlBuilder builder) { builder.append(render(object)); } } public CommitListTable(Styles styles, String idColName) { super(100, GWT.<Resources>create(Resources.class)); styles_ = styles; graphTheme_ = new GraphTheme(styles.graphLineImg()); graphCol_ = new CommitColumn(new GraphRenderer(graphTheme_)); addColumn(graphCol_); CommitColumn subjectCol = new CommitColumn(new SubjectRenderer()); addColumn(subjectCol, "Subject"); TextColumn<CommitInfo> authorCol = new TextColumn<CommitInfo>() { @Override public String getValue(CommitInfo object) { return object.getAuthor(); } }; addColumn(authorCol, "Author"); TextColumn<CommitInfo> dateCol = new TextColumn<CommitInfo>() { @Override public String getValue(CommitInfo object) { return DateTimeFormat.getFormat( PredefinedFormat.DATE_SHORT).format(object.getDate()); } }; addColumn(dateCol, "Date"); TextColumn<CommitInfo> idCol = new TextColumn<CommitInfo>() { @Override public String getValue(CommitInfo object) { return object.getId(); } }; addColumn(idCol, idColName); setColumnWidth(graphCol_, "0"); setColumnWidth(idCol, "100px"); setColumnWidth(subjectCol, "67%"); setColumnWidth(authorCol, "33%"); setColumnWidth(dateCol, "100px"); selectionModel_ = new SingleSelectionModel<CommitInfo>(); setSelectionModel(selectionModel_); } private void updateGraphColumnWidth() { int width = 0; for (CommitInfo commit : getVisibleItems()) { if (!StringUtil.isNullOrEmpty(commit.getGraph())) { width = Math.max( width, new GraphLine(commit.getGraph()).getTotalWidth(graphTheme_)); } } if (width > 0) setColumnWidth(graphCol_, (width + 12) + "px"); else setColumnWidth(graphCol_, "0"); } @Override public void setRowData(int start, List<? extends CommitInfo> values) { if (selectionModel_.getSelectedObject() != null) { selectionModel_.setSelected(selectionModel_.getSelectedObject(), false); } super.setRowData(start, values); updateGraphColumnWidth(); maybePreselectFirstRow(); } private void maybePreselectFirstRow() { if (!autoSelectFirstRow_) return; Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { if (getVisibleItemCount() > 0 && (selectionModel_.getSelectedObject() == null || selectionModel_.getSelectedObject().getId().equals(getVisibleItem(0).getId()))) { selectionModel_.setSelected(getVisibleItem(0), true); } } }); } public HandlerRegistration addSelectionChangeHandler(SelectionChangeEvent.Handler handler) { return selectionModel_.addSelectionChangeHandler(handler); } @Override public CommitInfo getSelectedCommit() { return selectionModel_.getSelectedObject(); } @Override public void clearSelection() { if (selectionModel_.getSelectedObject() != null) selectionModel_.setSelected(selectionModel_.getSelectedObject(), false); } @Override public void setAutoSelectFirstRow(boolean autoSelect) { autoSelectFirstRow_ = autoSelect; } private final SingleSelectionModel<CommitInfo> selectionModel_; private final Styles styles_; private CommitColumn graphCol_; private GraphTheme graphTheme_; private boolean autoSelectFirstRow_ = true; }