blob: 76d58125b719d827bd85f850159b5e3f73beac97 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.intellij.dvcs;
import com.intellij.dvcs.ui.DvcsBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.NonFocusableCheckBox;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.*;
import java.util.List;
public abstract class DvcsCommitAdditionalComponent implements RefreshableOnComponent {
private static final Logger log = Logger.getInstance(DvcsCommitAdditionalComponent.class);
protected final JPanel myPanel;
protected final JCheckBox myAmend;
@Nullable private String myPreviousMessage;
@Nullable private String myAmendedMessage;
@NotNull protected final CheckinProjectPanel myCheckinPanel;
@Nullable private Map<VirtualFile, String> myMessagesForRoots;
public DvcsCommitAdditionalComponent(@NotNull final Project project, @NotNull CheckinProjectPanel panel) {
myCheckinPanel = panel;
myPanel = new JPanel(new GridBagLayout());
final Insets insets = new Insets(2, 2, 2, 2);
// add amend checkbox
GridBagConstraints c = new GridBagConstraints();
//todo change to MigLayout
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 2;
c.anchor = GridBagConstraints.CENTER;
c.insets = insets;
c.weightx = 1;
c.fill = GridBagConstraints.HORIZONTAL;
myAmend = new NonFocusableCheckBox(DvcsBundle.message("commit.amend"));
myAmend.setMnemonic('m');
myAmend.setToolTipText(DvcsBundle.message("commit.amend.tooltip"));
myPreviousMessage = myCheckinPanel.getCommitMessage();
myAmend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (myAmend.isSelected()) {
if (myPreviousMessage.equals(myCheckinPanel.getCommitMessage())) { // if user has already typed something, don't revert it
if (myMessagesForRoots == null) {
loadMessagesInModalTask(project); //load all commit messages for all repositories
}
String message = constructAmendedMessage();
if (!StringUtil.isEmptyOrSpaces(message)) {
myAmendedMessage = message;
substituteCommitMessage(myAmendedMessage);
}
}
}
else {
// there was the amended message, but user has changed it => not reverting
if (myCheckinPanel.getCommitMessage().equals(myAmendedMessage)) {
myCheckinPanel.setCommitMessage(myPreviousMessage);
}
}
}
});
myPanel.add(myAmend, c);
}
private String constructAmendedMessage() {
Set<VirtualFile> selectedRoots = getVcsRoots(getSelectedFilePaths()); // get only selected files
LinkedHashSet<String> messages = ContainerUtil.newLinkedHashSet();
if (myMessagesForRoots != null) {
for (VirtualFile root : selectedRoots) {
String message = myMessagesForRoots.get(root);
if (message != null) {
messages.add(message);
}
}
}
return DvcsUtil.joinMessagesOrNull(messages);
}
public JComponent getComponent() {
return myPanel;
}
public void refresh() {
myAmend.setSelected(false);
}
private void loadMessagesInModalTask(@NotNull Project project) {
try {
myMessagesForRoots =
ProgressManager.getInstance().runProcessWithProgressSynchronously(new ThrowableComputable<Map<VirtualFile,String>, VcsException>() {
@Override
public Map<VirtualFile, String> compute() throws VcsException {
return getLastCommitMessages();
}
}, "Reading commit message...", false, project);
}
catch (VcsException e) {
Messages.showErrorDialog(getComponent(), "Couldn't load commit message of the commit to amend.\n" + e.getMessage(),
"Commit Message not Loaded");
log.info(e);
}
}
private void substituteCommitMessage(@NotNull String newMessage) {
myPreviousMessage = myCheckinPanel.getCommitMessage();
if (!myPreviousMessage.trim().equals(newMessage.trim())) {
myCheckinPanel.setCommitMessage(newMessage);
}
}
@Nullable
private Map<VirtualFile, String> getLastCommitMessages() throws VcsException {
Map<VirtualFile, String> messagesForRoots = new HashMap<VirtualFile, String>();
Collection<VirtualFile> roots = myCheckinPanel.getRoots(); //all committed vcs roots, not only selected
final Ref<VcsException> exception = Ref.create();
for (VirtualFile root : roots) {
String message = getLastCommitMessage(root);
messagesForRoots.put(root, message);
}
if (!exception.isNull()) {
throw exception.get();
}
return messagesForRoots;
}
@NotNull
private List<FilePath> getSelectedFilePaths() {
return ContainerUtil.map(myCheckinPanel.getFiles(), new Function<File, FilePath>() {
@Override
public FilePath fun(File file) {
return new FilePathImpl(file, file.isDirectory());
}
});
}
@NotNull
protected abstract Set<VirtualFile> getVcsRoots(@NotNull Collection<FilePath> files);
@Nullable
protected abstract String getLastCommitMessage(@NotNull VirtualFile repo) throws VcsException;
public boolean isAmend() {
return myAmend.isSelected();
}
}