blob: cbe6da5379cfa3414368e1be5f29f917b772511c [file] [log] [blame]
/*
* Copyright 2000-2013 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.editor.impl;
import com.intellij.ide.DataManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionButtonLook;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.impl.ActionButton;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.EditorMouseAdapter;
import com.intellij.openapi.editor.event.EditorMouseEvent;
import com.intellij.openapi.editor.event.EditorMouseMotionAdapter;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
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;
/**
* @author spleaner
*/
public class ContextMenuImpl extends JPanel implements Disposable {
@NonNls
public static final String ACTION_GROUP = "EditorContextBarMenu";
private final JComponent myComponent;
private final JLayeredPane myLayeredPane;
private ActionGroup myActionGroup;
private boolean myVisible = false;
private boolean myShow = false;
private int myCurrentOpacity;
private Timer myTimer;
private EditorImpl myEditor;
private boolean myDisposed;
private ActionToolbar myActionToolbar;
public ContextMenuImpl(JLayeredPane layeredPane, @NotNull final JScrollPane container, @NotNull final EditorImpl editor) {
setLayout(new BorderLayout(0, 0));
myEditor = editor;
myLayeredPane = layeredPane;
final ActionManager actionManager = ActionManager.getInstance();
editor.addEditorMouseListener(new EditorMouseAdapter() {
@Override
public void mouseExited(final EditorMouseEvent e) {
if (!isInsideActivationArea(container, e.getMouseEvent().getPoint())) {
toggleContextToolbar(false);
}
}
});
editor.addEditorMouseMotionListener(new EditorMouseMotionAdapter() {
@Override
public void mouseMoved(final EditorMouseEvent e) {
toggleContextToolbar(isInsideActivationArea(container, e.getMouseEvent().getPoint()));
}
});
AnAction action = actionManager.getAction(ACTION_GROUP);
if (action == null) {
action = new DefaultActionGroup();
actionManager.registerAction(ACTION_GROUP, action);
}
if (action instanceof ActionGroup) {
myActionGroup = (ActionGroup)action;
}
myComponent = createComponent();
add(myComponent);
setVisible(false);
setOpaque(false);
}
private static boolean isInsideActivationArea(JScrollPane container, Point p) {
final JViewport viewport = container.getViewport();
final Rectangle r = viewport.getBounds();
final Point viewPosition = viewport.getViewPosition();
final Rectangle activationArea = new Rectangle(0, 0, r.width, 150);
return activationArea.contains(p.x, p.y - viewPosition.y);
}
public static boolean mayShowToolbar(@Nullable final Document document) {
if (document == null) {
return false;
}
final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
return file != null && file.isValid() && (file.isInLocalFileSystem() || file instanceof HttpVirtualFile);
}
private void toggleContextToolbar(final boolean show) {
final Component toolbar = myComponent.getComponent(0);
final int count = ((Container)toolbar).getComponentCount();
if (count == 0) {
return;
}
if (myShow != show) {
myShow = show;
if (myShow && myActionToolbar != null) {
myActionToolbar.updateActionsImmediately();
}
restartTimer();
}
}
private void restartTimer() {
if (myTimer != null && myTimer.isRunning()) {
myTimer.stop();
}
myTimer = UIUtil.createNamedTimer("Restart context menu", 500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (myDisposed) return;
if (myTimer != null && myTimer.isRunning()) myTimer.stop();
myTimer = UIUtil.createNamedTimer("Restart context menu now", 50, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (myShow) {
if (myVisible) {
scheduleHide();
return;
}
if (myLayeredPane.getIndexOf(ContextMenuImpl.this) == -1) {
myCurrentOpacity = 0;
myLayeredPane.add(ContextMenuImpl.this, JLayeredPane.POPUP_LAYER);
ContextMenuImpl.this.setVisible(true);
myLayeredPane.revalidate();
}
myCurrentOpacity += 20;
if (myCurrentOpacity >= 100) {
myCurrentOpacity = 100;
myVisible = true;
myTimer.stop();
scheduleHide();
}
repaint();
}
else {
if (!myVisible) {
if (myTimer != null && myTimer.isRunning()) myTimer.stop();
return;
}
myCurrentOpacity -= 20;
if (myCurrentOpacity <= 0) {
myCurrentOpacity = 0;
myVisible = false;
myLayeredPane.remove(ContextMenuImpl.this);
myLayeredPane.repaint();
}
repaint();
}
}
});
myTimer.setRepeats(true);
myTimer.start();
}
});
myTimer.setRepeats(false);
myTimer.start();
}
@Override
public void dispose() {
myDisposed = true;
myEditor = null;
if (myTimer != null) {
myTimer.stop();
myTimer = null;
}
}
private void scheduleHide() {
if (myTimer != null && myTimer.isRunning()) {
myTimer.stop();
}
myTimer = UIUtil.createNamedTimer("Hide context menu", 1500, new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (myDisposed) return;
if (myComponent.isVisible()) {
final PointerInfo pointerInfo = MouseInfo.getPointerInfo();
if (pointerInfo != null) {
final Point location = pointerInfo.getLocation();
SwingUtilities.convertPointFromScreen(location, myComponent);
if (!myComponent.getBounds().contains(location)) {
toggleContextToolbar(false);
}
else {
scheduleHide();
}
}
}
}
});
myTimer.setRepeats(false);
myTimer.start();
}
private ActionToolbar createToolbar(final ActionGroup group) {
final ActionToolbarImpl actionToolbar =
new ActionToolbarImpl(ActionPlaces.CONTEXT_TOOLBAR, group, true, DataManager.getInstance(), ActionManagerEx.getInstanceEx(),
KeymapManagerEx.getInstanceEx()) {
@Override
public ActionButton createToolbarButton(final AnAction action,
final ActionButtonLook look,
final String place,
final Presentation presentation,
final Dimension minimumSize) {
final ActionButton result = new ActionButton(action, presentation, place, minimumSize) {
@Override
public void paintComponent(final Graphics g) {
final ActionButtonLook look = getButtonLook();
look.paintBackground(g, this);
look.paintIcon(g, this, getIcon());
}
};
result.setLook(look);
return result;
}
};
actionToolbar.setTargetComponent(myEditor.getContentComponent());
return actionToolbar;
}
private JComponent createComponent() {
myActionToolbar = createToolbar(myActionGroup);
myActionToolbar.setMinimumButtonSize(new Dimension(20, 20));
myActionToolbar.setReservePlaceAutoPopupIcon(false);
ContextMenuPanel contextMenuPanel = new ContextMenuPanel(this);
contextMenuPanel.setLayout(new BorderLayout(0, 0));
JComponent toolbarComponent = myActionToolbar.getComponent();
toolbarComponent.setOpaque(false);
contextMenuPanel.add(toolbarComponent);
return contextMenuPanel;
}
private static class ContextMenuPanel extends JPanel {
private final ContextMenuImpl myContextMenu;
private ContextMenuPanel(final ContextMenuImpl contextMenu) {
myContextMenu = contextMenu;
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
setOpaque(false);
}
@Override
protected void paintChildren(final Graphics g) {
Graphics2D graphics = (Graphics2D)g.create();
try {
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, myContextMenu.myCurrentOpacity / 100.0f));
super.paintChildren(graphics);
}
finally {
graphics.dispose();
}
}
@Override
public void paint(Graphics g) {
paintComponent(g);
super.paint(g);
}
@Override
public void paintComponent(final Graphics g) {
Rectangle r = getBounds();
Graphics2D graphics = (Graphics2D)g.create();
try {
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, myContextMenu.myCurrentOpacity / 500.0f));
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setColor(Color.GRAY);
graphics.fillRoundRect(0, 0, r.width - 1, r.height - 1, 6, 6);
}
finally {
graphics.dispose();
}
}
}
}