/*
* 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.ide.actions;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.ide.DataManager;
import com.intellij.ide.ui.search.BooleanOptionDescription;
import com.intellij.ide.ui.search.OptionDescription;
import com.intellij.ide.util.gotoByName.ChooseByNamePopup;
import com.intellij.ide.util.gotoByName.GotoActionItemProvider;
import com.intellij.ide.util.gotoByName.GotoActionModel;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.TransactionGuard;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Set;
public class GotoActionAction extends GotoActionBase implements DumbAware {
@Override
public void gotoActionPerformed(@NotNull final AnActionEvent e) {
final Project project = e.getData(CommonDataKeys.PROJECT);
final Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT);
Editor editor = e.getData(CommonDataKeys.EDITOR);
PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.popup.action");
GotoActionModel model = new GotoActionModel(project, component, editor, file);
GotoActionCallback<Object> callback = new GotoActionCallback<Object>() {
@Override
public void elementChosen(@NotNull ChooseByNamePopup popup, @NotNull Object element) {
String enteredText = popup.getEnteredText();
openOptionOrPerformAction(((GotoActionModel.MatchedValue)element).value, enteredText, project, component, e);
}
};
Pair<String, Integer> start = getInitialText(false, e);
showNavigationPopup(callback, null, createPopup(project, model, start.first, start.second, component, e), false);
}
@Nullable
private static ChooseByNamePopup createPopup(@Nullable Project project,
@NotNull final GotoActionModel model,
String initialText,
int initialIndex,
final Component component,
final AnActionEvent e) {
ChooseByNamePopup oldPopup = project == null ? null : project.getUserData(ChooseByNamePopup.CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY);
if (oldPopup != null) {
oldPopup.close(false);
}
final ChooseByNamePopup popup = new ChooseByNamePopup(project, model, new GotoActionItemProvider(model), oldPopup, initialText, false, initialIndex) {
@Override
protected void initUI(Callback callback, ModalityState modalityState, boolean allowMultipleSelection) {
super.initUI(callback, modalityState, allowMultipleSelection);
myList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
Object value = myList.getSelectedValue();
String text = getText(value);
if (text != null && myDropdownPopup != null) {
myDropdownPopup.setAdText(text, SwingConstants.LEFT);
}
}
@Nullable
private String getText(@Nullable Object o) {
if (o instanceof GotoActionModel.MatchedValue) {
GotoActionModel.MatchedValue mv = (GotoActionModel.MatchedValue)o;
if (mv.value instanceof BooleanOptionDescription ||
mv.value instanceof GotoActionModel.ActionWrapper && ((GotoActionModel.ActionWrapper)mv.value).getAction() instanceof ToggleAction) {
return "Press " + KeymapUtil.getKeystrokeText(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)) + " to toggle option";
}
}
return getAdText();
}
});
}
@NotNull
@Override
protected Set<Object> filter(@NotNull Set<Object> elements) {
return super.filter(model.sort(elements));
}
@Override
protected boolean closeForbidden(boolean ok) {
if (!ok) return false;
Object element = getChosenElement();
return element instanceof GotoActionModel.MatchedValue && processOptionInplace(((GotoActionModel.MatchedValue)element).value, this, component, e) ||
super.closeForbidden(true);
}
};
if (project != null) {
project.putUserData(ChooseByNamePopup.CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY, popup);
}
popup.addMouseClickListener(new MouseAdapter() {
@Override
public void mouseClicked(@NotNull MouseEvent me) {
Object element = popup.getSelectionByPoint(me.getPoint());
if (element instanceof GotoActionModel.MatchedValue) {
if (processOptionInplace(((GotoActionModel.MatchedValue)element).value, popup, component, e)) {
me.consume();
}
}
}
});
return popup;
}
private static boolean processOptionInplace(Object value, ChooseByNamePopup popup, Component component, AnActionEvent e) {
if (value instanceof BooleanOptionDescription) {
BooleanOptionDescription option = (BooleanOptionDescription)value;
option.setOptionState(!option.isOptionEnabled());
repaint(popup);
return true;
}
else if (value instanceof GotoActionModel.ActionWrapper) {
AnAction action = ((GotoActionModel.ActionWrapper)value).getAction();
if (action instanceof ToggleAction) {
performAction(action, component, e);
repaint(popup);
return true;
}
}
return false;
}
private static void repaint(@Nullable ChooseByNamePopup popup) {
if (popup != null) {
popup.repaintList();
}
}
public static void openOptionOrPerformAction(@NotNull Object element,
final String enteredText,
@Nullable final Project project,
Component component,
@Nullable AnActionEvent e) {
if (element instanceof OptionDescription) {
final String configurableId = ((OptionDescription)element).getConfigurableId();
TransactionGuard.getInstance().submitTransactionLater(project != null ? project : ApplicationManager.getApplication(), new Runnable() {
@Override
public void run() {
ShowSettingsUtilImpl.showSettingsDialog(project, configurableId, enteredText);
}
});
}
else {
performAction(element, component, e);
}
}
public static void performAction(Object element, @Nullable final Component component, @Nullable final AnActionEvent e) {
// element could be AnAction (SearchEverywhere)
final AnAction action = element instanceof AnAction ? (AnAction)element : ((GotoActionModel.ActionWrapper)element).getAction();
if (action != null) {
TransactionGuard.getInstance().submitTransactionLater(ApplicationManager.getApplication(), new Runnable() {
@Override
public void run() {
if (component == null) return;
Presentation presentation = action.getTemplatePresentation().clone();
DataContext context = DataManager.getInstance().getDataContext(component);
AnActionEvent event =
new AnActionEvent(e == null ? null : e.getInputEvent(), context, ActionPlaces.ACTION_SEARCH, presentation, ActionManager.getInstance(),
e == null ? 0 : e.getModifiers());
if (ActionUtil.lastUpdateAndCheckDumb(action, event, false)) {
if (action instanceof ActionGroup && ((ActionGroup)action).getChildren(event).length > 0) {
ListPopup popup = JBPopupFactory.getInstance()
.createActionGroupPopup(presentation.getText(), (ActionGroup)action, context, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);
if (component.isShowing()) {
popup.showInBestPositionFor(context);
}
else {
popup.showInFocusCenter();
}
}
else {
ActionUtil.performActionDumbAware(action, event);
}
}
}
});
}
}
@Override
protected boolean requiresProject() {
return false;
}
}