blob: 8ee9ee9206fbd0197bab54448474241958230d9f [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.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;
import javax.swing.*;
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 java.util.concurrent.atomic.AtomicBoolean;
import static com.intellij.ui.mac.foundation.Foundation.invoke;
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() {
}
@Nullable
public static ID findWindowForTitle(@Nullable String title) {
if (title == null || title.isEmpty()) return null;
final ID pool = invoke("NSAutoreleasePool", "new");
ID focusedWindow = null;
try {
final ID sharedApplication = invoke("NSApplication", "sharedApplication");
final ID windows = invoke(sharedApplication, "windows");
final ID windowEnumerator = invoke(windows, "objectEnumerator");
while (true) {
// dirty hack: walks through all the windows to find a cocoa window to show sheet for
final ID window = invoke(windowEnumerator, "nextObject");
if (0 == window.intValue()) break;
final ID windowTitle = invoke(window, "title");
if (windowTitle != null && windowTitle.intValue() != 0) {
final String titleString = toStringViaUTF8(windowTitle);
if (Comparing.equal(titleString, title)) {
focusedWindow = window;
break;
}
}
}
}
finally {
invoke(pool, "release");
}
return focusedWindow;
}
public static synchronized void startModal(JComponent component, String key) {
try {
if (SwingUtilities.isEventDispatchThread()) {
EventQueue theQueue = component.getToolkit().getSystemEventQueue();
while (component.getClientProperty(key) == Boolean.TRUE) {
AWTEvent event = theQueue.getNextEvent();
Object source = event.getSource();
if (event instanceof ActiveEvent) {
((ActiveEvent)event).dispatch();
}
else if (source instanceof Component) {
((Component)source).dispatchEvent(event);
}
else if (source instanceof MenuComponent) {
((MenuComponent)source).dispatchEvent(event);
}
else {
LOG.debug("Unable to dispatch: " + event);
}
}
}
else {
assert false: "Should be called from Event-Dispatch Thread only!";
while (component.getClientProperty(key) == Boolean.TRUE) {
// TODO:
//wait();
}
}
}
catch (InterruptedException ignored) {
}
}
public static synchronized void startModal(JComponent component) {
startModal(component, MAC_NATIVE_WINDOW_SHOWING);
}
public static boolean isFullKeyboardAccessEnabled() {
if (!SystemInfo.isMacOSSnowLeopard) return false;
final AtomicBoolean result = new AtomicBoolean();
Foundation.executeOnMainThread(new Runnable() {
@Override
public void run() {
result.set(invoke(invoke("NSApplication", "sharedApplication"), "isFullKeyboardAccessEnabled").intValue() == 1);
}
}, true, true);
return result.get();
}
public static void adjustFocusTraversal(@NotNull Disposable disposable) {
if (!SystemInfo.isMacOSSnowLeopard) return;
final AWTEventListener listener = new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent event) {
if (event instanceof KeyEvent
&& ((KeyEvent)event).getKeyCode() == KeyEvent.VK_TAB
&& (!(event.getSource() instanceof JTextComponent))
&& (!(event.getSource() instanceof JList))
&& !isFullKeyboardAccessEnabled())
((KeyEvent)event).consume();
}
};
Disposer.register(disposable, new Disposable() {
@Override
public void dispose() {
Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
}
});
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;
}
@Nullable
public static String getWindowTitle(Window documentRoot) {
String windowTitle = null;
if (documentRoot instanceof Frame) {
windowTitle = ((Frame)documentRoot).getTitle();
}
else if (documentRoot instanceof Dialog) {
windowTitle = ((Dialog)documentRoot).getTitle();
}
return windowTitle;
}
}