blob: d1573e2adc7fa6aa8e06eaa07cf2fa4f7bc53220 [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.ui.impl;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.impl.TypeSafeDataProviderAdapter;
import com.intellij.ide.ui.UISettings;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.application.impl.LaterInvocator;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.CommandProcessorEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.DialogWrapperDialog;
import com.intellij.openapi.ui.DialogWrapperPeer;
import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.ui.popup.StackingPopupDispatcher;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.*;
import com.intellij.openapi.wm.ex.LayoutFocusTraversalPolicyExt;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.openapi.wm.impl.IdeGlassPaneImpl;
import com.intellij.reference.SoftReference;
import com.intellij.ui.*;
import com.intellij.ui.components.JBLayeredPane;
import com.intellij.ui.mac.foundation.Foundation;
import com.intellij.ui.mac.foundation.ID;
import com.intellij.ui.mac.foundation.MacUtil;
import com.intellij.util.ReflectionUtil;
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.*;
import java.awt.image.BufferStrategy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTrackbackProvider {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.ui.DialogWrapper");
private final DialogWrapper myWrapper;
private AbstractDialog myDialog;
private boolean myCanBeParent = true;
private WindowManagerEx myWindowManager;
private final List<Runnable> myDisposeActions = new ArrayList<Runnable>();
private Project myProject;
private final ActionCallback myWindowFocusedCallback = new ActionCallback("DialogFocusedCallback");
private final ActionCallback myTypeAheadDone = new ActionCallback("DialogTypeAheadDone");
private ActionCallback myTypeAheadCallback;
protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, @Nullable Project project, boolean canBeParent, @NotNull DialogWrapper.IdeModalityType ideModalityType) {
myWrapper = wrapper;
myTypeAheadCallback = myWrapper.isTypeAheadEnabled() ? new ActionCallback() : null;
myWindowManager = null;
Application application = ApplicationManager.getApplication();
if (application != null && application.hasComponent(WindowManager.class)) {
myWindowManager = (WindowManagerEx)WindowManager.getInstance();
}
Window window = null;
if (myWindowManager != null) {
if (project == null) {
//noinspection deprecation
project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext());
}
myProject = project;
window = myWindowManager.suggestParentWindow(project);
if (window == null) {
Window focusedWindow = myWindowManager.getMostRecentFocusedWindow();
if (focusedWindow instanceof IdeFrameImpl) {
window = focusedWindow;
}
}
if (window == null) {
IdeFrame[] frames = myWindowManager.getAllProjectFrames();
for (IdeFrame frame : frames) {
if (frame instanceof IdeFrameImpl && ((IdeFrameImpl)frame).isActive()) {
window = (IdeFrameImpl)frame;
break;
}
}
}
}
Window owner;
if (window != null) {
owner = window;
}
else {
if (!isHeadless()) {
owner = JOptionPane.getRootFrame();
} else {
owner = null;
}
}
createDialog(owner, canBeParent, ideModalityType);
}
/**
* Creates modal <code>DialogWrapper</code>. The currently active window will be the dialog's parent.
*
* @param project parent window for the dialog will be calculated based on focused window for the
* specified <code>project</code>. This parameter can be <code>null</code>. In this case parent window
* will be suggested based on current focused window.
* @param canBeParent specifies whether the dialog can be parent for other windows. This parameter is used
* by <code>WindowManager</code>.
*/
protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, @Nullable Project project, boolean canBeParent) {
this(wrapper, project, canBeParent, DialogWrapper.IdeModalityType.IDE);
}
protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, boolean canBeParent) {
this(wrapper, (Project)null, canBeParent);
}
@Override
public boolean isHeadless() {
return isHeadlessEnv();
}
@Override
public Object[] getCurrentModalEntities() {
return LaterInvocator.getCurrentModalEntities();
}
public static boolean isHeadlessEnv() {
Application app = ApplicationManager.getApplication();
if (app == null) return GraphicsEnvironment.isHeadless();
return app.isUnitTestMode() || app.isHeadlessEnvironment();
}
/**
* @param parent parent component which is used to calculate heavy weight window ancestor.
* <code>parent</code> cannot be <code>null</code> and must be showing.
*/
protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, @NotNull Component parent, boolean canBeParent) {
myWrapper = wrapper;
if (!parent.isShowing() && parent != JOptionPane.getRootFrame()) {
throw new IllegalArgumentException("parent must be showing: " + parent);
}
myWindowManager = null;
Application application = ApplicationManager.getApplication();
if (application != null && application.hasComponent(WindowManager.class)) {
myWindowManager = (WindowManagerEx)WindowManager.getInstance();
}
Window owner = parent instanceof Window ? (Window)parent : (Window)SwingUtilities.getAncestorOfClass(Window.class, parent);
if (!(owner instanceof Dialog) && !(owner instanceof Frame)) {
owner = JOptionPane.getRootFrame();
}
createDialog(owner, canBeParent);
}
public DialogWrapperPeerImpl(@NotNull final DialogWrapper wrapper,final Window owner, final boolean canBeParent,
final DialogWrapper.IdeModalityType ideModalityType ) {
myWrapper = wrapper;
myWindowManager = null;
Application application = ApplicationManager.getApplication();
if (application != null && application.hasComponent(WindowManager.class)) {
myWindowManager = (WindowManagerEx)WindowManager.getInstance();
}
createDialog(owner, canBeParent);
if (!isHeadless()) {
Dialog.ModalityType modalityType = DialogWrapper.IdeModalityType.IDE.toAwtModality();
if (Registry.is("ide.perProjectModality")) {
modalityType = ideModalityType.toAwtModality();
}
myDialog.setModalityType(modalityType);
}
}
/** @see DialogWrapper#DialogWrapper(boolean, boolean)
*/
@Deprecated
public DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, final boolean canBeParent, final boolean applicationModalIfPossible) {
this(wrapper, null, canBeParent, applicationModalIfPossible);
}
@Deprecated
public DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper,final Window owner, final boolean canBeParent, final boolean applicationModalIfPossible) {
this(wrapper, owner, canBeParent, applicationModalIfPossible ? DialogWrapper.IdeModalityType.IDE : DialogWrapper.IdeModalityType.PROJECT);
}
@Override
public void setUndecorated(boolean undecorated) {
myDialog.setUndecorated(undecorated);
}
@Override
public void addMouseListener(MouseListener listener) {
myDialog.addMouseListener(listener);
}
@Override
public void addMouseListener(MouseMotionListener listener) {
myDialog.addMouseMotionListener(listener);
}
@Override
public void addKeyListener(KeyListener listener) {
myDialog.addKeyListener(listener);
}
private void createDialog(@Nullable Window owner, boolean canBeParent, @NotNull DialogWrapper.IdeModalityType ideModalityType) {
if (isHeadless()) {
myDialog = new HeadlessDialog();
return;
}
myDialog = new MyDialog(owner, myWrapper, myProject, myWindowFocusedCallback, myTypeAheadDone, myTypeAheadCallback);
myDialog.setModalityType(ideModalityType.toAwtModality());
myCanBeParent = canBeParent;
}
private void createDialog(@Nullable Window owner, boolean canBeParent) {
createDialog(owner, canBeParent, DialogWrapper.IdeModalityType.IDE);
}
@Override
public void toFront() {
myDialog.toFront();
}
@Override
public void toBack() {
myDialog.toBack();
}
@Override
@SuppressWarnings("SSBasedInspection")
protected void dispose() {
LOG.assertTrue(EventQueue.isDispatchThread(), "Access is allowed from event dispatch thread only");
for (Runnable runnable : myDisposeActions) {
runnable.run();
}
myDisposeActions.clear();
final JRootPane root = myDialog.getRootPane();
Runnable disposer = new Runnable() {
@Override
public void run() {
myDialog.dispose();
myProject = null;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (myDialog != null && root != null) {
myDialog.remove(root);
}
}
});
}
};
if (EventQueue.isDispatchThread()) {
disposer.run();
}
else {
SwingUtilities.invokeLater(disposer);
}
}
private boolean isProgressDialog() {
return myWrapper.isModalProgress();
}
@Override
@Nullable
public Container getContentPane() {
return getRootPane() != null ? myDialog.getContentPane() : null;
}
/**
* @see javax.swing.JDialog#validate
*/
@Override
public void validate() {
myDialog.validate();
}
/**
* @see javax.swing.JDialog#repaint
*/
@Override
public void repaint() {
myDialog.repaint();
}
@Override
public Window getOwner() {
return myDialog.getOwner();
}
@Override
public Window getWindow() {
return myDialog.getWindow();
}
@Override
public JRootPane getRootPane() {
return myDialog.getRootPane();
}
@Override
public Dimension getSize() {
return myDialog.getSize();
}
@Override
public String getTitle() {
return myDialog.getTitle();
}
/**
* @see java.awt.Window#pack
*/
@Override
public void pack() {
myDialog.pack();
}
@Override
public void setAppIcons() {
AppUIUtil.updateWindowIcon(getWindow());
}
@Override
public Dimension getPreferredSize() {
return myDialog.getPreferredSize();
}
@Override
public void setModal(boolean modal) {
myDialog.setModal(modal);
}
@Override
public boolean isModal() {
return myDialog.isModal();
}
@Override
public boolean isVisible() {
return myDialog.isVisible();
}
@Override
public boolean isShowing() {
return myDialog.isShowing();
}
@Override
public void setSize(int width, int height) {
myDialog.setSize(width, height);
}
@Override
public void setTitle(String title) {
myDialog.setTitle(title);
}
@Override
public void isResizable() {
myDialog.isResizable();
}
@Override
public void setResizable(boolean resizable) {
myDialog.setResizable(resizable);
}
@NotNull
@Override
public Point getLocation() {
return myDialog.getLocation();
}
@Override
public void setLocation(@NotNull Point p) {
myDialog.setLocation(p);
}
@Override
public void setLocation(int x, int y) {
myDialog.setLocation(x, y);
}
@Override
public ActionCallback show() {
LOG.assertTrue(EventQueue.isDispatchThread(), "Access is allowed from event dispatch thread only");
if (myTypeAheadCallback != null) {
IdeFocusManager.getInstance(myProject).typeAheadUntil(myTypeAheadCallback);
} LOG.assertTrue(EventQueue.isDispatchThread(), "Access is allowed from event dispatch thread only");
final ActionCallback result = new ActionCallback();
final AnCancelAction anCancelAction = new AnCancelAction();
final JRootPane rootPane = getRootPane();
anCancelAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)), rootPane);
myDisposeActions.add(new Runnable() {
@Override
public void run() {
anCancelAction.unregisterCustomShortcutSet(rootPane);
}
});
if (!myCanBeParent && myWindowManager != null) {
myWindowManager.doNotSuggestAsParent(myDialog.getWindow());
}
final CommandProcessorEx commandProcessor =
ApplicationManager.getApplication() != null ? (CommandProcessorEx)CommandProcessor.getInstance() : null;
final boolean appStarted = commandProcessor != null;
if (myDialog.isModal() && !isProgressDialog()) {
if (appStarted) {
commandProcessor.enterModal();
LaterInvocator.enterModal(myDialog);
}
}
if (appStarted) {
hidePopupsIfNeeded();
}
try {
myDialog.show();
}
finally {
if (myDialog.isModal() && !isProgressDialog()) {
if (appStarted) {
commandProcessor.leaveModal();
LaterInvocator.leaveModal(myDialog);
}
}
myDialog.getFocusManager().doWhenFocusSettlesDown(result.createSetDoneRunnable());
}
return result;
}
//hopefully this whole code will go away
private void hidePopupsIfNeeded() {
if (!SystemInfo.isMac) return;
StackingPopupDispatcher.getInstance().hidePersistentPopups();
myDisposeActions.add(new Runnable() {
@Override
public void run() {
StackingPopupDispatcher.getInstance().restorePersistentPopups();
}
});
}
@Override
public FocusTrackback getFocusTrackback() {
return myDialog.getFocusTrackback();
}
private class AnCancelAction extends AnAction implements DumbAware {
@Override
public void update(AnActionEvent e) {
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
e.getPresentation().setEnabled(false);
if (focusOwner instanceof JComponent && SpeedSearchBase.hasActiveSpeedSearch((JComponent)focusOwner)) {
return;
}
if (StackingPopupDispatcher.getInstance().isPopupFocused()) return;
if (focusOwner instanceof JTree) {
JTree tree = (JTree)focusOwner;
if (!tree.isEditing()) {
e.getPresentation().setEnabled(true);
}
}
else if (focusOwner instanceof JTable) {
JTable table = (JTable)focusOwner;
if (!table.isEditing()) {
e.getPresentation().setEnabled(true);
}
}
}
@Override
public void actionPerformed(AnActionEvent e) {
myWrapper.doCancelAction(e.getInputEvent());
}
}
private static class MyDialog extends JDialog implements DialogWrapperDialog, DataProvider, FocusTrackback.Provider, Queryable, AbstractDialog {
private final WeakReference<DialogWrapper> myDialogWrapper;
/**
* Initial size of the dialog. When the dialog is being closed and
* current size of the dialog is not equals to the initial size then the
* current (changed) size is stored in the <code>DimensionService</code>.
*/
private Dimension myInitialSize;
private String myDimensionServiceKey;
private boolean myOpened = false;
private boolean myActivated = false;
private FocusTrackback myFocusTrackback;
private MyDialog.MyWindowListener myWindowListener;
private final WeakReference<Project> myProject;
private final ActionCallback myFocusedCallback;
private final ActionCallback myTypeAheadDone;
private final ActionCallback myTypeAheadCallback;
private MyComponentListener myComponentListener;
public MyDialog(Window owner,
DialogWrapper dialogWrapper,
Project project,
@NotNull ActionCallback focused,
@NotNull ActionCallback typeAheadDone,
ActionCallback typeAheadCallback) {
super(owner);
myDialogWrapper = new WeakReference<DialogWrapper>(dialogWrapper);
myProject = project != null ? new WeakReference<Project>(project) : null;
setFocusTraversalPolicy(new LayoutFocusTraversalPolicyExt() {
@Override
protected boolean accept(Component aComponent) {
if (UIUtil.isFocusProxy(aComponent)) return false;
return super.accept(aComponent);
}
});
myFocusedCallback = focused;
myTypeAheadDone = typeAheadDone;
myTypeAheadCallback = typeAheadCallback;
final long typeAhead = getDialogWrapper().getTypeAheadTimeoutMs();
if (typeAhead <= 0) {
myTypeAheadDone.setDone();
}
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
myWindowListener = new MyWindowListener();
addWindowListener(myWindowListener);
myComponentListener = new MyComponentListener();
addComponentListener(myComponentListener);
}
@Override
public JDialog getWindow() {
return this;
}
@Override
public void putInfo(@NotNull Map<String, String> info) {
info.put("dialog", getTitle());
}
@Override
public FocusTrackback getFocusTrackback() {
return myFocusTrackback;
}
@Override
public DialogWrapper getDialogWrapper() {
return myDialogWrapper.get();
}
@Override
public void centerInParent() {
setLocationRelativeTo(getOwner());
}
@Override
public Object getData(String dataId) {
final DialogWrapper wrapper = myDialogWrapper.get();
if (wrapper instanceof DataProvider) {
return ((DataProvider)wrapper).getData(dataId);
}
else if (wrapper instanceof TypeSafeDataProvider) {
TypeSafeDataProviderAdapter adapter = new TypeSafeDataProviderAdapter((TypeSafeDataProvider)wrapper);
return adapter.getData(dataId);
}
return null;
}
@Override
public void setSize(int width, int height) {
_setSizeForLocation(width, height, null);
}
private void _setSizeForLocation(int width, int height, @Nullable Point initial) {
Point location = initial != null ? initial : getLocation();
Rectangle rect = new Rectangle(location.x, location.y, width, height);
ScreenUtil.fitToScreen(rect);
if (initial != null || location.x != rect.x || location.y != rect.y) {
setLocation(rect.x, rect.y);
}
super.setSize(rect.width, rect.height);
}
@Override
public void setBounds(int x, int y, int width, int height) {
Rectangle rect = new Rectangle(x, y, width, height);
ScreenUtil.fitToScreen(rect);
super.setBounds(rect.x, rect.y, rect.width, rect.height);
}
@Override
public void setBounds(Rectangle r) {
ScreenUtil.fitToScreen(r);
super.setBounds(r);
}
@Override
protected JRootPane createRootPane() {
return new DialogRootPane();
}
@Override
@SuppressWarnings("deprecation")
public void show() {
myFocusTrackback = new FocusTrackback(getDialogWrapper(), getParent(), true);
final DialogWrapper dialogWrapper = getDialogWrapper();
boolean isAutoAdjustable = dialogWrapper.isAutoAdjustable();
Point location = null;
if (isAutoAdjustable) {
pack();
Dimension packedSize = getSize();
Dimension minSize = getMinimumSize();
setSize(Math.max(packedSize.width, minSize.width), Math.max(packedSize.height, minSize.height));
setSize((int)(getWidth() * dialogWrapper.getHorizontalStretch()), (int)(getHeight() * dialogWrapper.getVerticalStretch()));
// Restore dialog's size and location
myDimensionServiceKey = dialogWrapper.getDimensionKey();
if (myDimensionServiceKey != null) {
final Project projectGuess = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(this));
location = DimensionService.getInstance().getLocation(myDimensionServiceKey, projectGuess);
Dimension size = DimensionService.getInstance().getSize(myDimensionServiceKey, projectGuess);
if (size != null) {
myInitialSize = new Dimension(size);
_setSizeForLocation(myInitialSize.width, myInitialSize.height, location);
}
}
if (myInitialSize == null) {
myInitialSize = getSize();
}
}
if (location == null) {
location = dialogWrapper.getInitialLocation();
}
if (location != null) {
setLocation(location);
}
else {
setLocationRelativeTo(getOwner());
}
if (isAutoAdjustable) {
final Rectangle bounds = getBounds();
ScreenUtil.fitToScreen(bounds);
setBounds(bounds);
}
addWindowListener(new WindowAdapter() {
@Override
public void windowActivated(WindowEvent e) {
final DialogWrapper wrapper = getDialogWrapper();
if (wrapper != null && myFocusTrackback != null) {
myFocusTrackback.cleanParentWindow();
myFocusTrackback.registerFocusComponent(new FocusTrackback.ComponentQuery() {
@Override
public Component getComponent() {
return wrapper.getPreferredFocusedComponent();
}
});
}
}
@Override
public void windowDeactivated(WindowEvent e) {
if (!isModal()) {
final Ref<IdeFocusManager> focusManager = new Ref<IdeFocusManager>(null);
Project project = getProject();
if (project != null && !project.isDisposed()) {
focusManager.set(getFocusManager());
focusManager.get().doWhenFocusSettlesDown(new Runnable() {
@Override
public void run() {
disposeFocusTrackbackIfNoChildWindowFocused(focusManager.get());
}
});
}
else {
disposeFocusTrackbackIfNoChildWindowFocused(focusManager.get());
}
}
}
@Override
public void windowOpened(WindowEvent e) {
if (!SystemInfo.isMacOSLion) return;
Window window = e.getWindow();
if (window instanceof Dialog) {
ID _native = MacUtil.findWindowForTitle(((Dialog)window).getTitle());
if (_native != null && _native.intValue() > 0) {
// see MacMainFrameDecorator
// NSCollectionBehaviorFullScreenAuxiliary = 1 << 8
Foundation.invoke(_native, "setCollectionBehavior:", 1 << 8);
}
}
}
});
if (Registry.is("actionSystem.fixLostTyping")) {
final IdeEventQueue queue = IdeEventQueue.getInstance();
if (queue != null) {
queue.getKeyEventDispatcher().resetState();
}
}
// Workaround for switching workspaces on dialog show
if (SystemInfo.isMac && myProject != null && Registry.is("ide.mac.fix.dialog.showing") && !dialogWrapper.isModalProgress()) {
final IdeFrame frame = WindowManager.getInstance().getIdeFrame(myProject.get());
AppIcon.getInstance().requestFocus(frame);
}
setBackground(UIUtil.getPanelBackground());
final ApplicationEx app = ApplicationManagerEx.getApplicationEx();
if (app != null && !app.isLoaded() && Splash.BOUNDS != null) {
final Point loc = getLocation();
loc.y = Splash.BOUNDS.y + Splash.BOUNDS.height;
setLocation(loc);
}
super.show();
}
@Nullable
private Project getProject() {
return SoftReference.dereference(myProject);
}
@Override
public IdeFocusManager getFocusManager() {
Project project = getProject();
if (project != null && !project.isDisposed()) {
return IdeFocusManager.getInstance(project);
}
else {
return IdeFocusManager.findInstance();
}
}
private void disposeFocusTrackbackIfNoChildWindowFocused(@Nullable IdeFocusManager focusManager) {
if (myFocusTrackback == null) return;
final DialogWrapper wrapper = myDialogWrapper.get();
if (wrapper == null || !wrapper.isShowing()) {
myFocusTrackback.dispose();
return;
}
if (focusManager != null) {
final Component c = focusManager.getFocusedDescendantFor(wrapper.getContentPane());
if (c == null) {
myFocusTrackback.dispose();
}
}
else {
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (owner == null || !SwingUtilities.isDescendingFrom(owner, wrapper.getContentPane())) {
myFocusTrackback.dispose();
}
}
}
@Override
@SuppressWarnings("deprecation")
public void hide() {
super.hide();
if (myFocusTrackback != null && !(myFocusTrackback.isSheduledForRestore() || myFocusTrackback.isWillBeSheduledForRestore())) {
myFocusTrackback.setWillBeSheduledForRestore();
IdeFocusManager mgr = getFocusManager();
Runnable r = new Runnable() {
@Override
public void run() {
if (myFocusTrackback != null) myFocusTrackback.restoreFocus();
myFocusTrackback = null;
}
};
mgr.doWhenFocusSettlesDown(r);
}
}
@Override
public void dispose() {
if (isShowing()) {
hide();
}
if (myWindowListener != null) {
myWindowListener.saveSize();
removeWindowListener(myWindowListener);
myWindowListener = null;
}
if (myComponentListener != null) {
removeComponentListener(myComponentListener);
myComponentListener = null;
}
if (myFocusTrackback != null && !(myFocusTrackback.isSheduledForRestore() || myFocusTrackback.isWillBeSheduledForRestore())) {
myFocusTrackback.dispose();
myFocusTrackback = null;
}
final BufferStrategy strategy = getBufferStrategy();
if (strategy != null) {
strategy.dispose();
}
super.dispose();
if (rootPane != null) { // Workaround for bug in native code to hold rootPane
try {
ReflectionUtil.resetField(rootPane.getClass(), null, "glassPane");
ReflectionUtil.resetField(rootPane.getClass(), null, "contentPane");
rootPane = null;
ReflectionUtil.resetField(Window.class, null, "windowListener");
}
catch (Exception ignored) {
}
}
// http://bugs.sun.com/view_bug.do?bug_id=6614056
try {
final List<?> list = ReflectionUtil.getField(Dialog.class, null, null, "modalDialogs");
list.remove(this);
}
catch (final Exception ignored) {
}
}
@Override
public Component getMostRecentFocusOwner() {
if (!myOpened) {
final DialogWrapper wrapper = getDialogWrapper();
if (wrapper != null) {
JComponent toFocus = wrapper.getPreferredFocusedComponent();
if (toFocus != null) {
return toFocus;
}
}
}
return super.getMostRecentFocusOwner();
}
@Override
public void paint(Graphics g) {
if (!SystemInfo.isMac || UIUtil.isUnderAquaLookAndFeel()) { // avoid rendering problems with non-aqua (alloy) LaFs under mac
// actually, it's a bad idea to globally enable this for dialog graphics since renderers, for example, may not
// inherit graphics so rendering hints won't be applied and trees or lists may render ugly.
UIUtil.applyRenderingHints(g);
}
super.paint(g);
}
@SuppressWarnings("SSBasedInspection")
private class MyWindowListener extends WindowAdapter {
@Override
public void windowClosing(WindowEvent e) {
DialogWrapper dialogWrapper = getDialogWrapper();
if (dialogWrapper.shouldCloseOnCross()) {
dialogWrapper.doCancelAction(e);
}
}
@Override
public void windowClosed(WindowEvent e) {
saveSize();
}
public void saveSize() {
if (myDimensionServiceKey != null &&
myInitialSize != null &&
myOpened) { // myInitialSize can be null only if dialog is disposed before first showing
final Project projectGuess = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(MyDialog.this));
// Save location
Point location = getLocation();
DimensionService.getInstance().setLocation(myDimensionServiceKey, location, projectGuess);
// Save size
Dimension size = getSize();
if (!myInitialSize.equals(size)) {
DimensionService.getInstance().setSize(myDimensionServiceKey, size, projectGuess);
}
myOpened = false;
}
}
@Override
public void windowOpened(WindowEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myOpened = true;
final DialogWrapper activeWrapper = getActiveWrapper();
if (activeWrapper == null) {
myFocusedCallback.setRejected();
myTypeAheadDone.setRejected();
}
}
});
}
@Override
public void windowActivated(final WindowEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
final DialogWrapper wrapper = getActiveWrapper();
if (wrapper == null && !myFocusedCallback.isProcessed()) {
myFocusedCallback.setRejected();
myTypeAheadDone.setRejected();
return;
}
if (myActivated) {
return;
}
myActivated = true;
JComponent toFocus = wrapper == null ? null : wrapper.getPreferredFocusedComponent();
if (toFocus == null) {
toFocus = getRootPane().getDefaultButton();
}
moveMousePointerOnButton(getRootPane().getDefaultButton());
setupSelectionOnPreferredComponent(toFocus);
if (toFocus != null) {
final JComponent toRequest = toFocus;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isShowing() && isActive()) {
getFocusManager().requestFocus(toRequest, true);
notifyFocused(wrapper);
}
}
});
} else {
if (isShowing()) {
notifyFocused(wrapper);
}
}
if (myTypeAheadCallback != null) {
myTypeAheadCallback.setDone();
}
}
});
}
private void notifyFocused(DialogWrapper wrapper) {
myFocusedCallback.setDone();
final long timeout = wrapper.getTypeAheadTimeoutMs();
if (timeout > 0) {
SimpleTimer.getInstance().setUp(new EdtRunnable() {
@Override
public void runEdt() {
myTypeAheadDone.setDone();
}
}, timeout);
}
}
private DialogWrapper getActiveWrapper() {
DialogWrapper activeWrapper = getDialogWrapper();
if (activeWrapper == null || !activeWrapper.isShowing()) {
return null;
}
return activeWrapper;
}
private void moveMousePointerOnButton(final JButton button) {
Application application = ApplicationManager.getApplication();
if (application != null && application.hasComponent(UISettings.class)) {
if (button != null && UISettings.getInstance().MOVE_MOUSE_ON_DEFAULT_BUTTON) {
Point p = button.getLocationOnScreen();
Rectangle r = button.getBounds();
try {
Robot robot = new Robot();
robot.mouseMove(p.x + r.width / 2, p.y + r.height / 2);
}
catch (AWTException e) {
LOG.warn(e);
}
}
}
}
}
private class MyComponentListener extends ComponentAdapter {
@Override
@SuppressWarnings({"RefusedBequest"})
public void componentResized(ComponentEvent e) {
if (getDialogWrapper().isAutoAdjustable()) {
UIUtil.adjustWindowToMinimumSize(getWindow());
}
}
}
private class DialogRootPane extends JRootPane implements DataProvider {
private final boolean myGlassPaneIsSet;
private DialogRootPane() {
setGlassPane(new IdeGlassPaneImpl(this));
myGlassPaneIsSet = true;
putClientProperty("DIALOG_ROOT_PANE", true);
}
@Override
protected JLayeredPane createLayeredPane() {
JLayeredPane p = new JBLayeredPane();
p.setName(this.getName()+".layeredPane");
return p;
}
@Override
public void setGlassPane(final Component glass) {
if (myGlassPaneIsSet) {
LOG.warn("Setting of glass pane for DialogWrapper is prohibited", new Exception());
return;
}
super.setGlassPane(glass);
}
@Override
public Object getData(@NonNls String dataId) {
final DialogWrapper wrapper = myDialogWrapper.get();
return wrapper != null && PlatformDataKeys.UI_DISPOSABLE.is(dataId) ? wrapper.getDisposable() : null;
}
}
private class MyFocusCommand extends FocusCommand implements KeyEventProcessor {
private Context myContextOnFinish;
private final List<KeyEvent> myEvents = new ArrayList<KeyEvent>();
private final DialogWrapper myWrapper;
private MyFocusCommand(DialogWrapper wrapper) {
myWrapper = getDialogWrapper();
setToInvalidateRequestors(false);
Disposer.register(wrapper.getDisposable(), new Disposable() {
@Override
public void dispose() {
if (!myTypeAheadDone.isProcessed()) {
myTypeAheadDone.setDone();
}
flushEvents();
}
});
}
@Override
@NotNull
public ActionCallback run() {
return myTypeAheadDone;
}
@Override
public KeyEventProcessor getProcessor() {
return this;
}
@Override
public Boolean dispatch(@NotNull KeyEvent e, @NotNull Context context) {
if (myWrapper == null || myTypeAheadDone.isProcessed()) return null;
myEvents.addAll(context.getQueue());
context.getQueue().clear();
if (isToDispatchToDialogNow(e)) {
return false;
} else {
myEvents.add(e);
return true;
}
}
private boolean isToDispatchToDialogNow(KeyEvent e) {
return e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_ESCAPE || e.getKeyCode() == KeyEvent.VK_TAB;
}
@Override
public void finish(@NotNull Context context) {
myContextOnFinish = context;
}
private void flushEvents() {
if (myWrapper.isToDispatchTypeAhead() && myContextOnFinish != null) {
myContextOnFinish.dispatch(myEvents);
}
}
}
}
private static void setupSelectionOnPreferredComponent(final JComponent component) {
if (component instanceof JTextField) {
JTextField field = (JTextField)component;
String text = field.getText();
if (text != null && field.getClientProperty(HAVE_INITIAL_SELECTION) == null) {
field.setSelectionStart(0);
field.setSelectionEnd(text.length());
}
}
else if (component instanceof JComboBox) {
JComboBox combobox = (JComboBox)component;
combobox.getEditor().selectAll();
}
}
@Override
public void setContentPane(JComponent content) {
myDialog.setContentPane(content);
}
@Override
public void centerInParent() {
myDialog.centerInParent();
}
public void setAutoRequestFocus(boolean b) {
UIUtil.setAutoRequestFocus((JDialog)myDialog, b);
}
}