blob: d7284ee5242f0bced996bfdfd5bc5f34557e9d9a [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.openapi.command.impl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandEvent;
import com.intellij.openapi.command.CommandListener;
import com.intellij.openapi.command.CommandProcessorEx;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Stack;
public class CoreCommandProcessor extends CommandProcessorEx {
private static class CommandDescriptor {
public final Runnable myCommand;
public final Project myProject;
public String myName;
public Object myGroupId;
public final Document myDocument;
public final UndoConfirmationPolicy myUndoConfirmationPolicy;
public CommandDescriptor(Runnable command,
Project project,
String name,
Object groupId,
UndoConfirmationPolicy undoConfirmationPolicy,
Document document) {
myCommand = command;
myProject = project;
myName = name;
myGroupId = groupId;
myUndoConfirmationPolicy = undoConfirmationPolicy;
myDocument = document;
}
@Override
public String toString() {
return "'" + myName + "', group: '" + myGroupId + "'";
}
}
protected CommandDescriptor myCurrentCommand = null;
private final Stack<CommandDescriptor> myInterruptedCommands = new Stack<CommandDescriptor>();
// private HashMap myStatisticsMap = new HashMap(); // command name --> count
// private HashMap myStatisticsMap = new HashMap(); // command name --> count
private final List<CommandListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private int myUndoTransparentCount = 0;
@Override
public void executeCommand(@NotNull Runnable runnable, String name, Object groupId) {
executeCommand(null, runnable, name, groupId);
}
@Override
public void executeCommand(Project project, @NotNull Runnable runnable, String name, Object groupId) {
executeCommand(project, runnable, name, groupId, UndoConfirmationPolicy.DEFAULT);
}
@Override
public void executeCommand(Project project, @NotNull Runnable runnable, String name, Object groupId, Document document) {
executeCommand(project, runnable, name, groupId, UndoConfirmationPolicy.DEFAULT, document);
}
@Override
public void executeCommand(Project project,
@NotNull final Runnable command,
final String name,
final Object groupId,
@NotNull UndoConfirmationPolicy confirmationPolicy) {
executeCommand(project, command, name, groupId, confirmationPolicy, null);
}
@Override
public void executeCommand(Project project,
@NotNull final Runnable command,
final String name,
final Object groupId,
@NotNull UndoConfirmationPolicy confirmationPolicy,
Document document) {
ApplicationManager.getApplication().assertIsDispatchThread();
if (project != null && project.isDisposed()) return;
if (CommandLog.LOG.isDebugEnabled()) {
CommandLog.LOG.debug("executeCommand: " + command + ", name = " + name + ", groupId = " + groupId);
}
if (myCurrentCommand != null) {
command.run();
return;
}
Throwable throwable = null;
try {
myCurrentCommand = new CommandDescriptor(command, project, name, groupId, confirmationPolicy, document);
fireCommandStarted();
command.run();
}
catch (Throwable th) {
throwable = th;
}
finally {
finishCommand(project, myCurrentCommand, throwable);
}
}
@Override
@Nullable
public Object startCommand(final Project project,
@Nls final String name,
final Object groupId,
final UndoConfirmationPolicy undoConfirmationPolicy) {
ApplicationManager.getApplication().assertIsDispatchThread();
if (project != null && project.isDisposed()) return null;
if (CommandLog.LOG.isDebugEnabled()) {
CommandLog.LOG.debug("startCommand: name = " + name + ", groupId = " + groupId);
}
if (myCurrentCommand != null) {
return null;
}
Document document = groupId instanceof Ref && ((Ref)groupId).get() instanceof Document ? (Document)((Ref)groupId).get() : null;
myCurrentCommand = new CommandDescriptor(EmptyRunnable.INSTANCE, project, name, groupId, undoConfirmationPolicy, document);
fireCommandStarted();
return myCurrentCommand;
}
@Override
public void finishCommand(final Project project, final Object command, final Throwable throwable) {
ApplicationManager.getApplication().assertIsDispatchThread();
CommandLog.LOG.assertTrue(myCurrentCommand != null, "no current command in progress");
fireCommandFinished();
}
protected void fireCommandFinished() {
ApplicationManager.getApplication().assertIsDispatchThread();
CommandDescriptor currentCommand = myCurrentCommand;
CommandEvent event = new CommandEvent(this, currentCommand.myCommand,
currentCommand.myName,
currentCommand.myGroupId,
currentCommand.myProject,
currentCommand.myUndoConfirmationPolicy,
currentCommand.myDocument);
try {
for (CommandListener listener : myListeners) {
try {
listener.beforeCommandFinished(event);
}
catch (Throwable e) {
CommandLog.LOG.error(e);
}
}
}
finally {
myCurrentCommand = null;
for (CommandListener listener : myListeners) {
try {
listener.commandFinished(event);
}
catch (Throwable e) {
CommandLog.LOG.error(e);
}
}
}
}
@Override
public void enterModal() {
ApplicationManager.getApplication().assertIsDispatchThread();
CommandDescriptor currentCommand = myCurrentCommand;
myInterruptedCommands.push(currentCommand);
if (currentCommand != null) {
fireCommandFinished();
}
}
@Override
public void leaveModal() {
ApplicationManager.getApplication().assertIsDispatchThread();
CommandLog.LOG.assertTrue(myCurrentCommand == null, "Command must not run: " + String.valueOf(myCurrentCommand));
myCurrentCommand = myInterruptedCommands.pop();
if (myCurrentCommand != null) {
fireCommandStarted();
}
}
@Override
public void setCurrentCommandName(String name) {
ApplicationManager.getApplication().assertIsDispatchThread();
CommandDescriptor currentCommand = myCurrentCommand;
CommandLog.LOG.assertTrue(currentCommand != null);
currentCommand.myName = name;
}
@Override
public void setCurrentCommandGroupId(Object groupId) {
ApplicationManager.getApplication().assertIsDispatchThread();
CommandDescriptor currentCommand = myCurrentCommand;
CommandLog.LOG.assertTrue(currentCommand != null);
currentCommand.myGroupId = groupId;
}
@Override
@Nullable
public Runnable getCurrentCommand() {
CommandDescriptor currentCommand = myCurrentCommand;
return currentCommand != null ? currentCommand.myCommand : null;
}
@Override
@Nullable
public String getCurrentCommandName() {
CommandDescriptor currentCommand = myCurrentCommand;
if (currentCommand != null) return currentCommand.myName;
if (!myInterruptedCommands.isEmpty()) {
final CommandDescriptor command = myInterruptedCommands.peek();
return command != null ? command.myName : null;
}
return null;
}
@Override
@Nullable
public Object getCurrentCommandGroupId() {
CommandDescriptor currentCommand = myCurrentCommand;
if (currentCommand != null) return currentCommand.myGroupId;
if (!myInterruptedCommands.isEmpty()) {
final CommandDescriptor command = myInterruptedCommands.peek();
return command != null ? command.myGroupId : null;
}
return null;
}
@Override
@Nullable
public Project getCurrentCommandProject() {
CommandDescriptor currentCommand = myCurrentCommand;
return currentCommand != null ? currentCommand.myProject : null;
}
@Override
public void addCommandListener(@NotNull CommandListener listener) {
myListeners.add(listener);
}
@Override
public void addCommandListener(@NotNull final CommandListener listener, @NotNull Disposable parentDisposable) {
addCommandListener(listener);
Disposer.register(parentDisposable, new Disposable() {
@Override
public void dispose() {
removeCommandListener(listener);
}
});
}
@Override
public void removeCommandListener(@NotNull CommandListener listener) {
myListeners.remove(listener);
}
@Override
public void runUndoTransparentAction(@NotNull Runnable action) {
if (myUndoTransparentCount++ == 0) fireUndoTransparentStarted();
try {
action.run();
}
finally {
if (--myUndoTransparentCount == 0) fireUndoTransparentFinished();
}
}
@Override
public boolean isUndoTransparentActionInProgress() {
return myUndoTransparentCount > 0;
}
@Override
public void markCurrentCommandAsGlobal(Project project) {
}
@Override
public void addAffectedDocuments(Project project, @NotNull Document... docs) {
}
@Override
public void addAffectedFiles(Project project, @NotNull VirtualFile... files) {
}
private void fireCommandStarted() {
ApplicationManager.getApplication().assertIsDispatchThread();
CommandDescriptor currentCommand = myCurrentCommand;
CommandEvent event = new CommandEvent(this,
currentCommand.myCommand,
currentCommand.myName,
currentCommand.myGroupId,
currentCommand.myProject,
currentCommand.myUndoConfirmationPolicy,
currentCommand.myDocument);
for (CommandListener listener : myListeners) {
try {
listener.commandStarted(event);
}
catch (Throwable e) {
CommandLog.LOG.error(e);
}
}
}
private void fireUndoTransparentStarted() {
for (CommandListener listener : myListeners) {
try {
listener.undoTransparentActionStarted();
}
catch (Throwable e) {
CommandLog.LOG.error(e);
}
}
}
private void fireUndoTransparentFinished() {
for (CommandListener listener : myListeners) {
try {
listener.undoTransparentActionFinished();
}
catch (Throwable e) {
CommandLog.LOG.error(e);
}
}
}
}