IDEA-102449 Mac OS + JDK 1.7: dialog sheets go to background if focus is moved from IDEA and then back [Filechooser part added and refactoring]

This commit is contained in:
Denis Fokin
2013-11-25 18:41:33 +04:00
parent f4303ebea7
commit ae9b9bab49
4 changed files with 129 additions and 50 deletions
@@ -0,0 +1,71 @@
/*
* 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.ui.mac;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.mac.foundation.Foundation;
import com.intellij.ui.mac.foundation.ID;
import com.sun.jna.Callback;
import com.sun.jna.Pointer;
import java.util.concurrent.atomic.AtomicInteger;
import static com.intellij.ui.mac.foundation.Foundation.invoke;
/**
* @author Denis Fokin
*/
public class JDK7WindowReorderingWorkaround {
private static AtomicInteger requestorCount = new AtomicInteger();
private static final Callback windowDidBecomeMainCallback = new Callback() {
@SuppressWarnings("UnusedDeclaration") // this is a native up-call
public void callback(ID self,
ID nsNotification)
{
if (requestorCount.intValue() == 0) {
invoke(self, "oldWindowDidBecomeMain:", nsNotification);
}
}
};
static {
if (SystemInfo.isJavaVersionAtLeast("1.7")) {
ID awtWindow = Foundation.getObjcClass("AWTWindow");
Pointer windowWillEnterFullScreenMethod = Foundation.createSelector("windowDidBecomeMain:");
ID originalWindowWillEnterFullScreen = Foundation.class_replaceMethod(awtWindow, windowWillEnterFullScreenMethod,
windowDidBecomeMainCallback, "v@::@");
Foundation.addMethodByID(awtWindow, Foundation.createSelector("oldWindowDidBecomeMain:"),
originalWindowWillEnterFullScreen, "v@::@");
}
}
static void disableReordering() {
if (SystemInfo.isJavaVersionAtLeast("1.7")) {
requestorCount.incrementAndGet();
}
}
static void enableReordering () {
if (SystemInfo.isJavaVersionAtLeast("1.7")) {
requestorCount.decrementAndGet();
}
}
}
@@ -137,6 +137,7 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
finally {
Foundation.cfRelease(self);
Foundation.cfRelease(contextInfo);
JDK7WindowReorderingWorkaround.enableReordering();
}
}
};
@@ -323,6 +324,7 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
ourImplMap.put(delegate, impl);
final ID select = toSelect == null ? null : Foundation.nsString(toSelect);
JDK7WindowReorderingWorkaround.disableReordering();
invoke(delegate, "performSelectorOnMainThread:withObject:waitUntilDone:", Foundation.createSelector("showOpenPanel:"), select, false);
}
@@ -36,7 +36,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@@ -72,6 +71,7 @@ public class MacMessagesImpl extends MacMessages {
resultsFromDocumentRoot.put(documentRoot, new MessageResult(returnCode.intValue(), suppressState.intValue() == 1));
queuesFromDocumentRoot.get(windowFromId.get(contextInfo.longValue())).runFromQueue();
}
JDK7WindowReorderingWorkaround.enableReordering();
cfRelease(self);
}
};
@@ -203,6 +203,7 @@ public class MacMessagesImpl extends MacMessages {
private MacMessagesImpl() {}
private static final Callback windowDidBecomeMainCallback = new Callback() {
@SuppressWarnings("UnusedDeclaration") // this is a native up-call
public void callback(ID self,
ID nsNotification)
{
@@ -357,6 +358,7 @@ public class MacMessagesImpl extends MacMessages {
private static void startModal(final Window w, ID windowId) {
long windowPtr = windowId.longValue();
synchronized (lock) {
JDK7WindowReorderingWorkaround.disableReordering();
windowFromId.put(windowPtr, w);
if (blockedDocumentRoots.keySet().contains(w)) {
blockedDocumentRoots.put(w, blockedDocumentRoots.get(w) + 1);
@@ -566,7 +568,7 @@ public class MacMessagesImpl extends MacMessages {
final Window documentRoot = getDocumentRootFromWindow(foremostWindow);
final ID nativeFocusedWindow = windowIdFromWindow(foremostWindow);
final ID nativeFocusedWindow = MacUtil.findWindowFromJavaWindow(foremostWindow);
paramsWrapper.setNativeWindow(nativeFocusedWindow);
@@ -591,54 +593,10 @@ public class MacMessagesImpl extends MacMessages {
return documentRoot;
}
private static ID windowIdFromWindow (Window w) {
ID windowId = null;
if (SystemInfo.isJavaVersionAtLeast("1.7") && Registry.is("skip.untitled.windows.for.mac.messages")) {
try {
Class <?> cWindowPeerClass = w.getPeer().getClass();
Method getPlatformWindowMethod = cWindowPeerClass.getDeclaredMethod("getPlatformWindow");
Object cPlatformWindow = getPlatformWindowMethod.invoke(w.getPeer());
Class <?> cPlatformWindowClass = cPlatformWindow.getClass();
Method getNSWindowPtrMethod = cPlatformWindowClass.getDeclaredMethod("getNSWindowPtr");
windowId = new ID((Long)getNSWindowPtrMethod.invoke(cPlatformWindow));
}
catch (NoSuchMethodException e) {
LOG.debug(e);
}
catch (InvocationTargetException e) {
LOG.debug(e);
}
catch (IllegalAccessException e) {
LOG.debug(e);
}
} else {
String foremostWindowTitle = getWindowTitle(w);
windowId = MacUtil.findWindowForTitle(foremostWindowTitle);
}
return windowId;
}
private static int convertReturnCodeFromNativeMessageDialog(int result) {
return result - 1000;
}
private static String getWindowTitle(Window documentRoot) {
String windowTitle;
if (documentRoot instanceof Frame) {
windowTitle = ((Frame)documentRoot).getTitle();
} else if (documentRoot instanceof Dialog) {
windowTitle = ((Dialog)documentRoot).getTitle();
} else {
throw new RuntimeException("The window is not a frame and not a dialog!");
}
return windowTitle;
}
@Messages.YesNoCancelResult
private static int convertReturnCodeFromNativeAlertDialog(int returnCode, String alternateText) {
// DEFAULT = 1
@@ -744,8 +702,8 @@ public class MacMessagesImpl extends MacMessages {
}
//Actually can, but not in this implementation. If you know a reasonable scenario, please ask Denis Fokin for the improvement.
LOG.assertTrue(getWindowTitle(_window) != null, "A window without a title should not be used for showing MacMessages");
while (_window != null && getWindowTitle(_window) == null) {
LOG.assertTrue(MacUtil.getWindowTitle(_window) != null, "A window without a title should not be used for showing MacMessages");
while (_window != null && MacUtil.getWindowTitle(_window) == null) {
_window = _window.getOwner();
//At least our frame should have a title
}
@@ -16,9 +16,11 @@
package com.intellij.ui.mac.foundation;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -27,6 +29,8 @@ import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static com.intellij.ui.mac.foundation.Foundation.invoke;
import static com.intellij.ui.mac.foundation.Foundation.toStringViaUTF8;
@@ -35,6 +39,7 @@ import static com.intellij.ui.mac.foundation.Foundation.toStringViaUTF8;
* @author pegov
*/
public class MacUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.ui.mac.foundation.MacUtil");
public static final String MAC_NATIVE_WINDOW_SHOWING = "MAC_NATIVE_WINDOW_SHOWING";
private MacUtil() {
@@ -72,7 +77,7 @@ public class MacUtil {
return focusedWindow;
}
public static synchronized void startModal(JComponent component, String key) {
try {
if (SwingUtilities.isEventDispatchThread()) {
@@ -91,7 +96,7 @@ public class MacUtil {
((MenuComponent)source).dispatchEvent(event);
}
else {
System.err.println("Unable to dispatch: " + event);
LOG.debug("Unable to dispatch: " + event);
}
}
}
@@ -137,4 +142,47 @@ public class MacUtil {
});
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.KEY_EVENT_MASK);
}
@SuppressWarnings("deprecation")
public static ID findWindowFromJavaWindow(final Window w) {
ID windowId = null;
if (SystemInfo.isJavaVersionAtLeast("1.7") && Registry.is("skip.untitled.windows.for.mac.messages")) {
try {
//noinspection deprecation
Class <?> cWindowPeerClass = w.getPeer().getClass();
Method getPlatformWindowMethod = cWindowPeerClass.getDeclaredMethod("getPlatformWindow");
Object cPlatformWindow = getPlatformWindowMethod.invoke(w.getPeer());
Class <?> cPlatformWindowClass = cPlatformWindow.getClass();
Method getNSWindowPtrMethod = cPlatformWindowClass.getDeclaredMethod("getNSWindowPtr");
windowId = new ID((Long)getNSWindowPtrMethod.invoke(cPlatformWindow));
}
catch (NoSuchMethodException e) {
LOG.debug(e);
}
catch (InvocationTargetException e) {
LOG.debug(e);
}
catch (IllegalAccessException e) {
LOG.debug(e);
}
} else {
String foremostWindowTitle = getWindowTitle(w);
windowId = findWindowForTitle(foremostWindowTitle);
}
return windowId;
}
public static String getWindowTitle(Window documentRoot) {
String windowTitle;
if (documentRoot instanceof Frame) {
windowTitle = ((Frame)documentRoot).getTitle();
} else if (documentRoot instanceof Dialog) {
windowTitle = ((Dialog)documentRoot).getTitle();
} else {
throw new RuntimeException("The window is not a frame and not a dialog!");
}
return windowTitle;
}
}