/*
* Copyright 2013-2014 Urs Wolfer
*
* 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.urswolfer.intellij.plugin.gerrit.push;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Map;
/**
* @author Urs Wolfer
*/
public class GerritPushExtensionPanel extends JPanel {
private static final Splitter COMMA_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private final boolean pushToGerritByDefault;
private JPanel indentedSettingPanel;
private JCheckBox pushToGerritCheckBox;
private JCheckBox draftChangeCheckBox;
private JCheckBox submitChangeCheckBox;
private JTextField branchTextField;
private JTextField topicTextField;
private JTextField reviewersTextField;
private JTextField ccTextField;
private Map<GerritPushTargetPanel, String> gerritPushTargetPanels = Maps.newHashMap();
private boolean initialized = false;
public GerritPushExtensionPanel(boolean pushToGerritByDefault) {
this.pushToGerritByDefault = pushToGerritByDefault;
createLayout();
pushToGerritCheckBox.setSelected(pushToGerritByDefault);
pushToGerritCheckBox.addActionListener(new SettingsStateActionListener());
setSettingsEnabled(pushToGerritCheckBox.isSelected());
addChangeListener();
}
public void registerGerritPushTargetPanel(GerritPushTargetPanel gerritPushTargetPanel, String branch) {
if (initialized) { // a new dialog gets initialized; start again
initialized = false;
gerritPushTargetPanels.clear();
}
if (branch != null) {
branch = branch.replaceAll("^refs/(for|drafts)/", "");
branch = branch.replaceAll("%.*$", "");
}
gerritPushTargetPanels.put(gerritPushTargetPanel, branch);
}
public void initialized() {
initialized = true;
if (gerritPushTargetPanels.size() == 1) {
branchTextField.setText(gerritPushTargetPanels.values().iterator().next());
}
// force a deferred update (changes are monitored only after full construction of dialog)
SwingUtilities.invokeLater(new Runnable() {
public void run() {
initDestinationBranch();
}
});
}
private void createLayout() {
JPanel mainPanel = new JPanel();
mainPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
pushToGerritCheckBox = new JCheckBox("Push to Gerrit");
mainPanel.add(pushToGerritCheckBox);
indentedSettingPanel = new JPanel(new GridLayoutManager(6, 2));
draftChangeCheckBox = new JCheckBox("Draft-Change");
draftChangeCheckBox.setToolTipText("Publish change as draft (reviewers cannot submit change).");
indentedSettingPanel.add(draftChangeCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null));
submitChangeCheckBox = new JCheckBox("Submit Change");
submitChangeCheckBox.setToolTipText("Changes can be directly submitted on push. This is primarily useful for " +
"teams that don't want to do code review but want to use Gerrit’s submit strategies to handle " +
"contention on busy branches. Using submit creates a change and submits it immediately, if the caller " +
"has submit permission.");
indentedSettingPanel.add(submitChangeCheckBox, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null));
branchTextField = addTextField(
"Branch:",
"The push destination branch.",
2);
topicTextField = addTextField(
"Topic:",
"A short tag associated with all of the changes in the same group, such as the local topic branch name.",
3);
reviewersTextField = addTextField(
"Reviewers (user names, comma separated):",
"Users which will be added as reviewers.",
4);
ccTextField = addTextField(
"CC (user names, comma separated):",
"Users which will receive carbon copies of the notification message.",
5);
final JPanel settingLayoutPanel = new JPanel();
settingLayoutPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
settingLayoutPanel.setLayout(new BoxLayout(settingLayoutPanel, BoxLayout.X_AXIS));
settingLayoutPanel.add(Box.createRigidArea(new Dimension(20, 0)));
settingLayoutPanel.add(indentedSettingPanel);
mainPanel.add(settingLayoutPanel);
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(mainPanel);
add(Box.createHorizontalGlue());
}
private JTextField addTextField(String label, String toolTipText, int row) {
indentedSettingPanel.add(
new JLabel(label),
new GridConstraints(row, 0, 1, 1,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_NONE,
GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_FIXED,
null, null, null)
);
JTextField textField = new JTextField();
textField.setToolTipText(toolTipText);
indentedSettingPanel.add(
textField,
new GridConstraints(row, 1, 1, 1,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_HORIZONTAL,
GridConstraints.SIZEPOLICY_WANT_GROW,
GridConstraints.SIZEPOLICY_FIXED,
new Dimension(250, 0), null, null)
);
return textField;
}
private void addChangeListener() {
ChangeActionListener gerritPushChangeListener = new ChangeActionListener();
pushToGerritCheckBox.addActionListener(gerritPushChangeListener);
draftChangeCheckBox.addActionListener(gerritPushChangeListener);
submitChangeCheckBox.addActionListener(gerritPushChangeListener);
ChangeTextActionListener gerritPushTextChangeListener = new ChangeTextActionListener();
branchTextField.getDocument().addDocumentListener(gerritPushTextChangeListener);
topicTextField.getDocument().addDocumentListener(gerritPushTextChangeListener);
reviewersTextField.getDocument().addDocumentListener(gerritPushTextChangeListener);
ccTextField.getDocument().addDocumentListener(gerritPushTextChangeListener);
}
public String getRef() {
String ref = "%s";
if (pushToGerritCheckBox.isSelected()) {
if (draftChangeCheckBox.isSelected()) {
ref = "refs/drafts/";
} else {
ref = "refs/for/";
}
if (!branchTextField.getText().isEmpty()) {
ref += branchTextField.getText();
} else {
ref += "%s";
}
List<String> gerritSpecs = Lists.newArrayList();
if (submitChangeCheckBox.isSelected()) {
gerritSpecs.add("submit");
}
if (!topicTextField.getText().isEmpty()) {
gerritSpecs.add("topic=" + topicTextField.getText());
}
handleCommaSeparatedUserNames(gerritSpecs, reviewersTextField, "r");
handleCommaSeparatedUserNames(gerritSpecs, ccTextField, "cc");
String gerritSpec = Joiner.on(',').join(gerritSpecs);
if (!Strings.isNullOrEmpty(gerritSpec)) {
ref += "%%" + gerritSpec;
}
}
return ref;
}
private void handleCommaSeparatedUserNames(List<String> gerritSpecs, JTextField textField, String option) {
Iterable<String> items = COMMA_SPLITTER.split(textField.getText());
for (String item : items) {
gerritSpecs.add(option + '=' + item);
}
}
private void initDestinationBranch() {
for (Map.Entry<GerritPushTargetPanel, String> entry : gerritPushTargetPanels.entrySet()) {
entry.getKey().initBranch(String.format(getRef(), entry.getValue()), pushToGerritByDefault);
}
}
private void updateDestinationBranch() {
for (Map.Entry<GerritPushTargetPanel, String> entry : gerritPushTargetPanels.entrySet()) {
entry.getKey().updateBranch(String.format(getRef(), entry.getValue()));
}
}
private void setSettingsEnabled(boolean enabled) {
UIUtil.setEnabled(indentedSettingPanel, enabled, true);
}
/**
* Updates destination branch text field after every config change.
*/
private class ChangeActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
updateDestinationBranch();
}
}
/**
* Updates destination branch text field after every text-field config change.
*/
private class ChangeTextActionListener implements DocumentListener {
@Override
public void insertUpdate(DocumentEvent e) {
handleChange();
}
@Override
public void removeUpdate(DocumentEvent e) {
handleChange();
}
@Override
public void changedUpdate(DocumentEvent e) {
handleChange();
}
private void handleChange() {
updateDestinationBranch();
}
}
/**
* Activates or deactivates settings according to checkbox.
*/
private class SettingsStateActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
setSettingsEnabled(pushToGerritCheckBox.isSelected());
}
}
}