/* * 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(); } }