blob: 606a2eab988667a4df4d4cd6fe9794897281cad5 [file] [log] [blame]
// Copyright (C) 2009 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.admin;
import static com.google.gerrit.client.ui.Util.highlight;
import com.google.gerrit.client.ConfirmationCallback;
import com.google.gerrit.client.ConfirmationDialog;
import com.google.gerrit.client.ErrorDialog;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.access.AccessMap;
import com.google.gerrit.client.access.ProjectAccessInfo;
import com.google.gerrit.client.actions.ActionButton;
import com.google.gerrit.client.info.ActionInfo;
import com.google.gerrit.client.info.GitwebInfo;
import com.google.gerrit.client.info.WebLinkInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.HintTextBox;
import com.google.gerrit.client.ui.Hyperlink;
import com.google.gerrit.client.ui.NavigationTable;
import com.google.gerrit.client.ui.OnEditEnabler;
import com.google.gerrit.client.ui.PagingHyperlink;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
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.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.globalkey.client.NpTextBox;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ProjectBranchesScreen extends PaginatedProjectScreen {
private Hyperlink prev;
private Hyperlink next;
private BranchesTable branchTable;
private Button delBranch;
private Button addBranch;
private HintTextBox nameTxtBox;
private HintTextBox irevTxtBox;
private FlowPanel addPanel;
private NpTextBox filterTxt;
private Query query;
public ProjectBranchesScreen(final Project.NameKey toShow) {
super(toShow);
}
@Override
public String getScreenToken() {
return PageLinks.toProjectBranches(getProjectKey());
}
@Override
protected void onLoad() {
super.onLoad();
addPanel.setVisible(false);
AccessMap.get(getProjectKey(),
new GerritCallback<ProjectAccessInfo>() {
@Override
public void onSuccess(ProjectAccessInfo result) {
addPanel.setVisible(result.canAddRefs());
}
});
query = new Query(match).start(start).run();
savedPanel = BRANCHES;
}
private void updateForm() {
branchTable.updateDeleteButton();
addBranch.setEnabled(true);
nameTxtBox.setEnabled(true);
irevTxtBox.setEnabled(true);
}
@Override
protected void onInitUI() {
super.onInitUI();
initPageHeader();
prev = PagingHyperlink.createPrev();
prev.setVisible(false);
next = PagingHyperlink.createNext();
next.setVisible(false);
addPanel = new FlowPanel();
final Grid addGrid = new Grid(2, 2);
addGrid.setStyleName(Gerrit.RESOURCES.css().addBranch());
final int texBoxLength = 50;
nameTxtBox = new HintTextBox();
nameTxtBox.setVisibleLength(texBoxLength);
nameTxtBox.setHintText(Util.C.defaultBranchName());
nameTxtBox.addKeyPressHandler(new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {
if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
doAddNewBranch();
}
}
});
addGrid.setText(0, 0, Util.C.columnBranchName() + ":");
addGrid.setWidget(0, 1, nameTxtBox);
irevTxtBox = new HintTextBox();
irevTxtBox.setVisibleLength(texBoxLength);
irevTxtBox.setHintText(Util.C.defaultRevisionSpec());
irevTxtBox.addKeyPressHandler(new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {
if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
doAddNewBranch();
}
}
});
addGrid.setText(1, 0, Util.C.initialRevision() + ":");
addGrid.setWidget(1, 1, irevTxtBox);
addBranch = new Button(Util.C.buttonAddBranch());
addBranch.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
doAddNewBranch();
}
});
addPanel.add(addGrid);
addPanel.add(addBranch);
branchTable = new BranchesTable();
delBranch = new Button(Util.C.buttonDeleteBranch());
delBranch.setStyleName(Gerrit.RESOURCES.css().branchTableDeleteButton());
delBranch.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
branchTable.deleteChecked();
}
});
HorizontalPanel buttons = new HorizontalPanel();
buttons.setStyleName(Gerrit.RESOURCES.css().branchTablePrevNextLinks());
buttons.add(delBranch);
buttons.add(prev);
buttons.add(next);
add(branchTable);
add(buttons);
add(addPanel);
}
private void initPageHeader() {
parseToken();
HorizontalPanel hp = new HorizontalPanel();
hp.setStyleName(Gerrit.RESOURCES.css().projectFilterPanel());
Label filterLabel = new Label(Util.C.projectFilter());
filterLabel.setStyleName(Gerrit.RESOURCES.css().projectFilterLabel());
hp.add(filterLabel);
filterTxt = new NpTextBox();
filterTxt.setValue(match);
filterTxt.addKeyUpHandler(new KeyUpHandler() {
@Override
public void onKeyUp(KeyUpEvent event) {
Query q = new Query(filterTxt.getValue());
if (match.equals(q.qMatch)) {
q.start(start);
} else {
if (query == null) {
q.run();
}
query = q;
}
}
});
hp.add(filterTxt);
add(hp);
}
private void doAddNewBranch() {
final String branchName = nameTxtBox.getText().trim();
if ("".equals(branchName)) {
nameTxtBox.setFocus(true);
return;
}
final String rev = irevTxtBox.getText().trim();
if ("".equals(rev)) {
irevTxtBox.setText("HEAD");
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
irevTxtBox.selectAll();
irevTxtBox.setFocus(true);
}
});
return;
}
addBranch.setEnabled(false);
ProjectApi.createBranch(getProjectKey(), branchName, rev,
new GerritCallback<BranchInfo>() {
@Override
public void onSuccess(BranchInfo branch) {
showAddedBranch(branch);
nameTxtBox.setText("");
irevTxtBox.setText("");
query = new Query(match).start(start).run();
}
@Override
public void onFailure(Throwable caught) {
addBranch.setEnabled(true);
selectAllAndFocus(nameTxtBox);
new ErrorDialog(caught.getMessage()).center();
}
});
}
void showAddedBranch(BranchInfo branch) {
SafeHtmlBuilder b = new SafeHtmlBuilder();
b.openElement("b");
b.append(Gerrit.C.branchCreationConfirmationMessage());
b.closeElement("b");
b.openElement("p");
b.append(branch.ref());
b.closeElement("p");
ConfirmationDialog confirmationDialog =
new ConfirmationDialog(Gerrit.C.branchCreationDialogTitle(),
b.toSafeHtml(), new ConfirmationCallback() {
@Override
public void onOk() {
//do nothing
}
});
confirmationDialog.center();
confirmationDialog.setCancelVisible(false);
}
private static void selectAllAndFocus(TextBox textBox) {
textBox.selectAll();
textBox.setFocus(true);
}
private class BranchesTable extends NavigationTable<BranchInfo> {
private ValueChangeHandler<Boolean> updateDeleteHandler;
boolean canDelete;
BranchesTable() {
table.setWidth("");
table.setText(0, 2, Util.C.columnBranchName());
table.setText(0, 3, Util.C.columnBranchRevision());
final FlexCellFormatter fmt = table.getFlexCellFormatter();
fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
updateDeleteHandler = new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
updateDeleteButton();
}
};
}
Set<String> getCheckedRefs() {
Set<String> refs = new HashSet<>();
for (int row = 1; row < table.getRowCount(); row++) {
final BranchInfo k = getRowItem(row);
if (k != null && table.getWidget(row, 1) instanceof CheckBox
&& ((CheckBox) table.getWidget(row, 1)).getValue()) {
refs.add(k.ref());
}
}
return refs;
}
void setChecked(Set<String> refs) {
for (int row = 1; row < table.getRowCount(); row++) {
final BranchInfo k = getRowItem(row);
if (k != null && refs.contains(k.ref()) &&
table.getWidget(row, 1) instanceof CheckBox) {
((CheckBox) table.getWidget(row, 1)).setValue(true);
}
}
}
void deleteChecked() {
final Set<String> refs = getCheckedRefs();
SafeHtmlBuilder b = new SafeHtmlBuilder();
b.openElement("b");
b.append(Gerrit.C.branchDeletionConfirmationMessage());
b.closeElement("b");
b.openElement("p");
boolean first = true;
for (String ref : refs) {
if (!first) {
b.append(",").br();
}
b.append(ref);
first = false;
}
b.closeElement("p");
if (refs.isEmpty()) {
updateDeleteButton();
return;
}
delBranch.setEnabled(false);
ConfirmationDialog confirmationDialog =
new ConfirmationDialog(Gerrit.C.branchDeletionDialogTitle(),
b.toSafeHtml(), new ConfirmationCallback() {
@Override
public void onOk() {
deleteBranches(refs);
}
@Override
public void onCancel() {
branchTable.updateDeleteButton();
}
});
confirmationDialog.center();
}
private void deleteBranches(final Set<String> branches) {
ProjectApi.deleteBranches(getProjectKey(), branches,
new GerritCallback<VoidResult>() {
@Override
public void onSuccess(VoidResult result) {
query = new Query(match).start(start).run();
}
@Override
public void onFailure(Throwable caught) {
query = new Query(match).start(start).run();
super.onFailure(caught);
}
});
}
void display(List<BranchInfo> branches) {
displaySubset(branches, 0, branches.size());
}
void displaySubset(List<BranchInfo> branches, int fromIndex, int toIndex) {
canDelete = false;
while (1 < table.getRowCount()) {
table.removeRow(table.getRowCount() - 1);
}
for (BranchInfo k : branches.subList(fromIndex, toIndex)) {
final int row = table.getRowCount();
table.insertRow(row);
applyDataRowStyle(row);
populate(row, k);
}
}
void populate(int row, BranchInfo k) {
GitwebInfo c = Gerrit.info().gitweb();
if (k.canDelete()) {
CheckBox sel = new CheckBox();
sel.addValueChangeHandler(updateDeleteHandler);
table.setWidget(row, 1, sel);
canDelete = true;
} else {
table.setText(row, 1, "");
}
table.setWidget(row, 2, new InlineHTML(highlight(k.getShortName(), match)));
if (k.revision() != null) {
if ("HEAD".equals(k.getShortName())) {
setHeadRevision(row, 3, k.revision());
} else {
table.setText(row, 3, k.revision());
}
} else {
table.setText(row, 3, "");
}
FlowPanel actionsPanel = new FlowPanel();
if (c != null) {
actionsPanel.add(new Anchor(c.getLinkName(), false,
c.toBranch(new Branch.NameKey(getProjectKey(), k.ref()))));
}
if (k.webLinks() != null) {
for (WebLinkInfo webLink : Natives.asList(k.webLinks())) {
actionsPanel.add(webLink.toAnchor());
}
}
if (k.actions() != null) {
k.actions().copyKeysIntoChildren("id");
for (ActionInfo a : Natives.asList(k.actions().values())) {
actionsPanel.add(new ActionButton(getProjectKey(), k, a));
}
}
table.setWidget(row, 4, actionsPanel);
final FlexCellFormatter fmt = table.getFlexCellFormatter();
String iconCellStyle = Gerrit.RESOURCES.css().iconCell();
String dataCellStyle = Gerrit.RESOURCES.css().dataCell();
if (RefNames.REFS_CONFIG.equals(k.getShortName())
|| "HEAD".equals(k.getShortName())) {
iconCellStyle = Gerrit.RESOURCES.css().specialBranchIconCell();
dataCellStyle = Gerrit.RESOURCES.css().specialBranchDataCell();
fmt.setStyleName(row, 0, iconCellStyle);
}
fmt.addStyleName(row, 1, iconCellStyle);
fmt.addStyleName(row, 2, dataCellStyle);
fmt.addStyleName(row, 3, dataCellStyle);
fmt.addStyleName(row, 4, dataCellStyle);
setRowItem(row, k);
}
private void setHeadRevision(final int row, final int column,
final String rev) {
AccessMap.get(getProjectKey(),
new GerritCallback<ProjectAccessInfo>() {
@Override
public void onSuccess(ProjectAccessInfo result) {
if (result.isOwner()) {
table.setWidget(row, column, getHeadRevisionWidget(rev));
} else {
table.setText(row, 3, rev);
}
}
});
}
private Widget getHeadRevisionWidget(final String headRevision) {
FlowPanel p = new FlowPanel();
final InlineLabel l = new InlineLabel(headRevision);
final Image edit = new Image(Gerrit.RESOURCES.edit());
edit.addStyleName(Gerrit.RESOURCES.css().editHeadButton());
final NpTextBox input = new NpTextBox();
input.setVisibleLength(35);
input.setValue(headRevision);
input.setVisible(false);
final Button save = new Button();
save.setText(Util.C.saveHeadButton());
save.setVisible(false);
save.setEnabled(false);
final Button cancel = new Button();
cancel.setText(Util.C.cancelHeadButton());
cancel.setVisible(false);
OnEditEnabler e = new OnEditEnabler(save);
e.listenTo(input);
edit.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
l.setVisible(false);
edit.setVisible(false);
input.setVisible(true);
save.setVisible(true);
cancel.setVisible(true);
}
});
save.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
save.setEnabled(false);
ProjectApi.setHead(getProjectKey(), input.getValue().trim(),
new GerritCallback<NativeString>() {
@Override
public void onSuccess(NativeString result) {
Gerrit.display(PageLinks.toProjectBranches(getProjectKey()));
}
@Override
public void onFailure(Throwable caught) {
super.onFailure(caught);
save.setEnabled(true);
}
});
}
});
cancel.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
l.setVisible(true);
edit.setVisible(true);
input.setVisible(false);
input.setValue(headRevision);
save.setVisible(false);
save.setEnabled(false);
cancel.setVisible(false);
}
});
p.add(l);
p.add(edit);
p.add(input);
p.add(save);
p.add(cancel);
return p;
}
boolean hasBranchCanDelete() {
return canDelete;
}
void updateDeleteButton() {
boolean on = false;
for (int row = 1; row < table.getRowCount(); row++) {
Widget w = table.getWidget(row, 1);
if (w != null && w instanceof CheckBox) {
CheckBox sel = (CheckBox) w;
if (sel.getValue()) {
on = true;
break;
}
}
}
delBranch.setEnabled(on);
}
@Override
protected void onOpenRow(int row) {
if (row > 0) {
movePointerTo(row);
}
}
@Override
protected Object getRowItemKey(BranchInfo item) {
return item.ref();
}
}
@Override
public void onShowView() {
super.onShowView();
if (match != null) {
filterTxt.setCursorPos(match.length());
}
filterTxt.setFocus(true);
}
private class Query {
private String qMatch;
private int qStart;
Query(String match) {
this.qMatch = match;
}
Query start(int start) {
this.qStart = start;
return this;
}
Query run() {
// Retrieve one more branch than page size to determine if there are more
// branches to display
ProjectApi.getBranches(getProjectKey(), pageSize + 1, qStart, qMatch,
new ScreenLoadCallback<JsArray<BranchInfo>>(ProjectBranchesScreen.this) {
@Override
public void preDisplay(JsArray<BranchInfo> result) {
if (!isAttached()) {
// View has been disposed.
} else if (query == Query.this) {
query = null;
showList(result);
} else {
query.run();
}
}
});
return this;
}
void showList(JsArray<BranchInfo> result) {
setToken(getTokenForScreen(qMatch, qStart));
ProjectBranchesScreen.this.match = qMatch;
ProjectBranchesScreen.this.start = qStart;
if (result.length() <= pageSize) {
branchTable.display(Natives.asList(result));
next.setVisible(false);
} else {
branchTable.displaySubset(Natives.asList(result), 0,
result.length() - 1);
setupNavigationLink(next, qMatch, qStart + pageSize);
}
if (qStart > 0) {
setupNavigationLink(prev, qMatch, qStart - pageSize);
} else {
prev.setVisible(false);
}
delBranch.setVisible(branchTable.hasBranchCanDelete());
Set<String> checkedRefs = branchTable.getCheckedRefs();
branchTable.setChecked(checkedRefs);
updateForm();
if (!isCurrentView()) {
display();
}
}
}
}