/*
* Copyright 2000-2016 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.openapi.vcs.checkin;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.todo.*;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.ActionPopupMenu;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.CommitExecutor;
import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.ui.components.labels.LinkListener;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;
import com.intellij.util.Consumer;
import com.intellij.util.PairConsumer;
import com.intellij.util.text.DateFormatUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
import java.awt.*;
import java.util.Collection;
import static com.intellij.CommonBundle.getCancelButtonText;
import static com.intellij.openapi.ui.Messages.*;
import static com.intellij.util.ui.UIUtil.getWarningIcon;
/**
* @author irengrig
* Date: 2/17/11
* Time: 5:54 PM
*/
public class TodoCheckinHandler extends CheckinHandler {
private final Project myProject;
private final CheckinProjectPanel myCheckinProjectPanel;
private final VcsConfiguration myConfiguration;
private TodoFilter myTodoFilter;
public TodoCheckinHandler(CheckinProjectPanel checkinProjectPanel) {
myProject = checkinProjectPanel.getProject();
myCheckinProjectPanel = checkinProjectPanel;
myConfiguration = VcsConfiguration.getInstance(myProject);
}
@Override
public RefreshableOnComponent getBeforeCheckinConfigurationPanel() {
JCheckBox checkBox = new JCheckBox(VcsBundle.message("before.checkin.new.todo.check", ""));
return new RefreshableOnComponent() {
@Override
public JComponent getComponent() {
JPanel panel = new JPanel(new BorderLayout(4, 0));
panel.add(checkBox, BorderLayout.WEST);
setFilterText(myConfiguration.myTodoPanelSettings.todoFilterName);
if (myConfiguration.myTodoPanelSettings.todoFilterName != null) {
myTodoFilter = TodoConfiguration.getInstance().getTodoFilter(myConfiguration.myTodoPanelSettings.todoFilterName);
}
Consumer<TodoFilter> consumer = todoFilter -> {
myTodoFilter = todoFilter;
String name = todoFilter == null ? null : todoFilter.getName();
myConfiguration.myTodoPanelSettings.todoFilterName = name;
setFilterText(name);
};
LinkLabel linkLabel = new LinkLabel("Configure", null);
linkLabel.setListener(new LinkListener() {
@Override
public void linkSelected(LinkLabel aSource, Object aLinkData) {
DefaultActionGroup group = SetTodoFilterAction.createPopupActionGroup(myProject, myConfiguration.myTodoPanelSettings, consumer);
ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.TODO_VIEW_TOOLBAR, group);
popupMenu.getComponent().show(linkLabel, 0, linkLabel.getHeight());
}
}, null);
panel.add(linkLabel, BorderLayout.CENTER);
CheckinHandlerUtil.disableWhenDumb(myProject, checkBox, "TODO check is impossible until indices are up-to-date");
return panel;
}
private void setFilterText(String filterName) {
if (filterName == null) {
checkBox.setText(VcsBundle.message("before.checkin.new.todo.check", IdeBundle.message("action.todo.show.all")));
} else {
checkBox.setText(VcsBundle.message("before.checkin.new.todo.check", "Filter: " + filterName));
}
}
@Override
public void refresh() {
}
@Override
public void saveState() {
myConfiguration.CHECK_NEW_TODO = checkBox.isSelected();
}
@Override
public void restoreState() {
checkBox.setSelected(myConfiguration.CHECK_NEW_TODO);
}
};
}
@Override
public ReturnResult beforeCheckin(@Nullable CommitExecutor executor, PairConsumer<Object, Object> additionalDataConsumer) {
if (! myConfiguration.CHECK_NEW_TODO) return ReturnResult.COMMIT;
if (DumbService.getInstance(myProject).isDumb()) {
String todoName = VcsBundle.message("before.checkin.new.todo.check.title");
if (Messages.showOkCancelDialog(myProject,
todoName +
" can't be performed while " + ApplicationNamesInfo.getInstance().getFullProductName() + " updates the indices in background.\n" +
"You can commit the changes without running checks, or you can wait until indices are built.",
todoName + " is not possible right now",
"&Wait", "&Commit", null) == Messages.OK) {
return ReturnResult.CANCEL;
}
return ReturnResult.COMMIT;
}
Collection<Change> changes = myCheckinProjectPanel.getSelectedChanges();
TodoCheckinHandlerWorker worker = new TodoCheckinHandlerWorker(myProject, changes, myTodoFilter, true);
Ref<Boolean> completed = Ref.create(Boolean.FALSE);
ProgressManager.getInstance().run(new Task.Modal(myProject, "Looking for New and Edited TODO Items...", true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setIndeterminate(true);
worker.execute();
}
@Override
public void onSuccess() {
completed.set(Boolean.TRUE);
}
});
if (completed.get() && (worker.getAddedOrEditedTodos().isEmpty() && worker.getInChangedTodos().isEmpty() &&
worker.getSkipped().isEmpty())) return ReturnResult.COMMIT;
if (!completed.get()) return ReturnResult.CANCEL;
return showResults(worker, executor);
}
private ReturnResult showResults(TodoCheckinHandlerWorker worker, CommitExecutor executor) {
String commitButtonText = executor != null ? executor.getActionText() : myCheckinProjectPanel.getCommitActionName();
commitButtonText = StringUtil.trimEnd(commitButtonText, "...");
String text = createMessage(worker);
boolean thereAreTodoFound = worker.getAddedOrEditedTodos().size() + worker.getInChangedTodos().size() > 0;
String title = "TODO";
if (thereAreTodoFound) {
return askReviewOrCommit(worker, commitButtonText, text, title);
}
else if (YES == showYesNoDialog(myProject, text, title, commitButtonText, getCancelButtonText(), getWarningIcon())) {
return ReturnResult.COMMIT;
}
return ReturnResult.CANCEL;
}
@NotNull
private ReturnResult askReviewOrCommit(@NotNull TodoCheckinHandlerWorker worker,
@NotNull String commitButton,
@NotNull String text,
@NotNull String title) {
String yesButton = VcsBundle.message("todo.in.new.review.button");
switch (showYesNoCancelDialog(myProject, text, title, yesButton, commitButton, getCancelButtonText(), getWarningIcon())) {
case YES:
showTodo(worker);
return ReturnResult.CLOSE_WINDOW;
case NO:
return ReturnResult.COMMIT;
}
return ReturnResult.CANCEL;
}
private void showTodo(TodoCheckinHandlerWorker worker) {
String title = "For commit (" + DateFormatUtil.formatDateTime(System.currentTimeMillis()) + ")";
ServiceManager.getService(myProject, TodoView.class).addCustomTodoView(new TodoTreeBuilderFactory() {
@Override
public TodoTreeBuilder createTreeBuilder(JTree tree, DefaultTreeModel treeModel, Project project) {
return new CustomChangelistTodosTreeBuilder(tree, treeModel, myProject, title, worker.inOneList());
}
}, title, new TodoPanelSettings(myConfiguration.myTodoPanelSettings));
ApplicationManager.getApplication().invokeLater(() -> {
ToolWindowManager manager = ToolWindowManager.getInstance(myProject);
if (manager != null) {
ToolWindow window = manager.getToolWindow("TODO");
if (window != null) {
window.show(() -> {
ContentManager cm = window.getContentManager();
Content[] contents = cm.getContents();
if (contents.length > 0) {
cm.setSelectedContent(contents[contents.length - 1], true);
}
});
}
}
}, ModalityState.NON_MODAL, myProject.getDisposed());
}
private static String createMessage(TodoCheckinHandlerWorker worker) {
int added = worker.getAddedOrEditedTodos().size();
int changed = worker.getInChangedTodos().size();
int skipped = worker.getSkipped().size();
if (added == 0 && changed == 0) {
return VcsBundle.message("todo.handler.only.skipped", skipped);
} else if (changed == 0) {
return VcsBundle.message("todo.handler.only.added", added, skipped);
} else if (added == 0) {
return VcsBundle.message("todo.handler.only.in.changed", changed, skipped);
} else {
return VcsBundle.message("todo.handler.only.both", added, changed, skipped);
}
}
}