blob: 1ffa4e261858229993bcdc1b3a00069b7d2d9239 [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;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.impl.ModalityHelper;
import com.intellij.ui.mac.foundation.ID;
import com.intellij.ui.mac.foundation.MacUtil;
import com.intellij.util.ui.UIUtil;
import com.sun.jna.Callback;
import com.sun.jna.Pointer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import static com.intellij.ui.mac.foundation.Foundation.*;
/**
* @author pegov
*/
public class MacMessagesImpl extends MacMessages {
private static final Logger LOG = Logger.getInstance("#com.intellij.ui.mac.MacMessages");
private static class MessageResult {
MessageResult (int returnCode, boolean suppress) {
myReturnCode = returnCode;
mySuppress = suppress;
}
private final int myReturnCode;
private final boolean mySuppress;
}
private static final Map<Window, MessageResult> resultsFromDocumentRoot = new HashMap<Window, MessageResult> ();
private static final Map<Window, MacMessagesQueue<Runnable>> queuesFromDocumentRoot = new HashMap<Window, MacMessagesQueue<Runnable>>();
private static final Callback SHEET_DID_END = new Callback() {
@SuppressWarnings("UnusedDeclaration")
public void callback(ID self, String selector, ID alert, ID returnCode, ID contextInfo) {
synchronized (lock) {
Window documentRoot = windowFromId.get(contextInfo.longValue());
processResult(documentRoot);
ID suppressState = invoke(invoke(alert, "suppressionButton"), "state");
resultsFromDocumentRoot.put(documentRoot, new MessageResult(returnCode.intValue(), suppressState.intValue() == 1));
queuesFromDocumentRoot.get(windowFromId.get(contextInfo.longValue())).runFromQueue();
}
JDK7WindowReorderingWorkaround.enableReordering();
cfRelease(self);
}
};
private static final Callback VARIABLE_BUTTONS_SHEET_PANEL = new Callback() {
@SuppressWarnings("UnusedDeclaration")
public void callback(ID self, String selector, ID params) {
ID title = invoke(params, "objectAtIndex:", 0);
ID message = invoke(params, "objectAtIndex:", 1);
ID focusedWindow = invoke(params, "objectAtIndex:", 2);
ID alertStyle = invoke(params, "objectAtIndex:", 4);
ID doNotAskText = invoke(params, "objectAtIndex:", 5);
int defaultOptionIndex = Integer.parseInt(toStringViaUTF8(invoke(params, "objectAtIndex:", 6)));
int focusedOptionIndex = Integer.parseInt(toStringViaUTF8(invoke(params, "objectAtIndex:", 7)));
ID buttons = invoke(params, "objectAtIndex:", 8);
ID doNotAskChecked = invoke(params, "objectAtIndex:", 9);
ID alert = invoke(invoke("NSAlert", "alloc"), "init");
invoke(alert, "setMessageText:", title);
invoke(alert, "setInformativeText:", message);
if ("error".equals(toStringViaUTF8(alertStyle))) {
invoke(alert, "setAlertStyle:", 2); // NSCriticalAlertStyle = 2
}
final ID buttonEnumerator = invoke(buttons, "objectEnumerator");
while (true) {
final ID button = invoke(buttonEnumerator, "nextObject");
if (0 == button.intValue()) break;
invoke(alert, "addButtonWithTitle:", button);
}
if (defaultOptionIndex != -1) {
invoke(invoke(alert, "window"), "setDefaultButtonCell:",
invoke(invoke(invoke(alert, "buttons"), "objectAtIndex:", defaultOptionIndex), "cell"));
}
// it seems like asking for focus will cause java to go and query focus owner too, which may cause dead locks on main-thread
//if (focusedOptionIndex != -1) {
// invoke(invoke(alert, "window"), "makeFirstResponder:",
// invoke(invoke(alert, "buttons"), "objectAtIndex:", focusedOptionIndex));
//} else {
// int count = invoke(buttons, "count").intValue();
// invoke(invoke(alert, "window"), "makeFirstResponder:",
// invoke(invoke(alert, "buttons"), "objectAtIndex:", count == 1 ? 0 : 1));
//}
enableEscapeToCloseTheMessage(alert);
String doNotAsk = toStringViaUTF8(doNotAskText);
if (!"-1".equals(doNotAsk)) {
invoke(alert, "setShowsSuppressionButton:", 1);
invoke(invoke(alert, "suppressionButton"), "setTitle:", doNotAskText);
invoke(invoke(alert, "suppressionButton"), "setState:", "checked".equals(toStringViaUTF8(doNotAskChecked)));
}
invoke(alert, "beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:", focusedWindow, self,
createSelector("alertDidEnd:returnCode:contextInfo:"), focusedWindow);
cfRelease(alert);
}
};
private static final Callback SIMPLE_SHEET_PANEL = new Callback() {
@SuppressWarnings("UnusedDeclaration")
public void callback(ID self, String selector, ID params) {
ID title = invoke(params, "objectAtIndex:", 0);
ID defaultText = invoke(params, "objectAtIndex:", 1);
ID otherText = invoke(params, "objectAtIndex:", 2);
ID alternateText = invoke(params, "objectAtIndex:", 3);
ID message = invoke(params, "objectAtIndex:", 4);
ID focusedWindow = invoke(params, "objectAtIndex:", 5);
ID alertStyle = invoke(params, "objectAtIndex:", 7);
ID doNotAskText = invoke(params, "objectAtIndex:", 8);
ID doNotAskChecked = invoke(params, "objectAtIndex:", 9);
boolean alternateExist = !"-1".equals(toStringViaUTF8(alternateText));
boolean otherExist = !"-1".equals(toStringViaUTF8(otherText));
final ID alert = invoke("NSAlert", "alertWithMessageText:defaultButton:alternateButton:otherButton:informativeTextWithFormat:",
title, defaultText, alternateExist ? alternateText : null, otherExist ? otherText : null, message);
if ("error".equals(toStringViaUTF8(alertStyle))) {
invoke(alert, "setAlertStyle:", 2); // NSCriticalAlertStyle = 2
}
// it seems like asking for focus will cause java to go and query focus owner too, which may cause dead locks on main-thread
//ID window = invoke(alert, "window");
//invoke(window, "makeFirstResponder:",
// invoke(invoke(alert, "buttons"), "objectAtIndex:", alternateExist ? 2 : otherExist ? 1 : 0));
//
if (!alternateExist) {
enableEscapeToCloseTheMessage(alert);
}
String doNotAsk = toStringViaUTF8(doNotAskText);
if (!"-1".equals(doNotAsk)) {
invoke(alert, "setShowsSuppressionButton:", 1);
invoke(invoke(alert, "suppressionButton"), "setTitle:", doNotAskText);
invoke(invoke(alert, "suppressionButton"), "setState:", "checked".equals(toStringViaUTF8(doNotAskChecked)));
}
invoke(alert, "beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:", focusedWindow, self,
createSelector("alertDidEnd:returnCode:contextInfo:"), focusedWindow);
}
};
private static void processResult(Window w) {
synchronized (lock) {
if (!blockedDocumentRoots.keySet().contains(w)) {
throw new RuntimeException("Window should be in th list.");
}
int openedSheetsForWindow = blockedDocumentRoots.get(w);
if (openedSheetsForWindow < 1) {
throw new RuntimeException("We should have at least one window in the list");
}
if (openedSheetsForWindow == 1) {
// The last sheet
blockedDocumentRoots.remove(w);
} else {
blockedDocumentRoots.put(w, openedSheetsForWindow - 1);
}
}
}
private static void enableEscapeToCloseTheMessage(ID alert) {
int buttonsNumber = invoke(invoke(alert, "buttons"), "count").intValue();
if (buttonsNumber < 2) return;
invoke(invoke(invoke(alert, "buttons"), "objectAtIndex:",
buttonsNumber - 1), "setKeyEquivalent:", nsString("\033"));
}
private MacMessagesImpl() {}
private static final Callback windowDidBecomeMainCallback = new Callback() {
@SuppressWarnings("UnusedDeclaration") // this is a native up-call
public void callback(ID self,
ID nsNotification)
{
synchronized (lock) {
if (!windowFromId.keySet().contains(self.longValue())) {
return;
}
}
invoke(self, "oldWindowDidBecomeMain:", nsNotification);
}
};
static {
if (SystemInfo.isMac) {
final ID delegateClass = allocateObjcClassPair(getObjcClass("NSObject"), "NSAlertDelegate_");
if (!addMethod(delegateClass, createSelector("alertDidEnd:returnCode:contextInfo:"), SHEET_DID_END, "v*")) {
throw new RuntimeException("Unable to add method to objective-c delegate class!");
}
if (!addMethod(delegateClass, createSelector("showSheet:"), SIMPLE_SHEET_PANEL, "v*")) {
throw new RuntimeException("Unable to add method to objective-c delegate class!");
}
if (!addMethod(delegateClass, createSelector("showVariableButtonsSheet:"), VARIABLE_BUTTONS_SHEET_PANEL, "v*")) {
throw new RuntimeException("Unable to add method to objective-c delegate class!");
}
registerObjcClassPair(delegateClass);
if (SystemInfo.isJavaVersionAtLeast("1.7")) {
ID awtWindow = getObjcClass("AWTWindow");
Pointer windowWillEnterFullScreenMethod = createSelector("windowDidBecomeMain:");
ID originalWindowWillEnterFullScreen = class_replaceMethod(awtWindow, windowWillEnterFullScreenMethod,
windowDidBecomeMainCallback, "v@::@");
addMethodByID(awtWindow, createSelector("oldWindowDidBecomeMain:"),
originalWindowWillEnterFullScreen, "v@::@");
}
}
}
@Override
public void showOkMessageDialog(@NotNull String title, String message, @NotNull String okText, @Nullable Window window) {
showAlertDialog(title, okText, null, null, message, window);
}
@Override
public void showOkMessageDialog(@NotNull String title, String message, @NotNull String okText) {
showAlertDialog(title, okText, null, null, message, null);
}
@Override
@Messages.YesNoResult
public int showYesNoDialog(@NotNull String title, String message, @NotNull String yesButton, @NotNull String noButton, @Nullable Window window) {
return showAlertDialog(title, yesButton, null, noButton, message, window) == Messages.YES ? Messages.YES : Messages.NO;
}
@Override
@Messages.YesNoResult
public int showYesNoDialog(@NotNull String title, String message, @NotNull String yesButton, @NotNull String noButton, @Nullable Window window,
@Nullable DialogWrapper.DoNotAskOption doNotAskDialogOption) {
return showAlertDialog(title, yesButton, null, noButton, message, window, false, doNotAskDialogOption) == Messages.YES ? Messages.YES : Messages.NO;
}
@Override
public void showErrorDialog(@NotNull String title, String message, @NotNull String okButton, @Nullable Window window) {
showAlertDialog(title, okButton, null, null, message, window, true, null);
}
@Override
@Messages.YesNoCancelResult
public int showYesNoCancelDialog(@NotNull String title,
String message,
@NotNull String defaultButton,
String alternateButton,
String otherButton,
Window window,
@Nullable DialogWrapper.DoNotAskOption doNotAskOption) {
return showAlertDialog(title, defaultButton, alternateButton, otherButton, message, window, false, doNotAskOption);
}
private static final Object lock = new Object();
private static final HashMap<Window, Integer> blockedDocumentRoots = new HashMap<Window, Integer>();
private static final HashMap<Long, Window> windowFromId = new HashMap<Long, Window>();
public static void pumpEventsDocumentExclusively (Window documentRoot) {
Integer messageNumber = blockedDocumentRoots.get(documentRoot);
EventQueue theQueue = documentRoot.getToolkit().getSystemEventQueue();
do {
try {
AWTEvent event = theQueue.getNextEvent();
boolean eventOk = true;
if (event instanceof InputEvent) {
final Object s = event.getSource();
if (s instanceof Component) {
Component c = (Component)s;
Window w = findDocumentRoot(c);
if (w == documentRoot) {
eventOk = false;
((InputEvent)event).consume();
}
}
}
if (eventOk) {
Class<?>[] paramString = new Class<?>[1];
paramString[0] = AWTEvent.class;
Method method = theQueue.getClass().getDeclaredMethod("dispatchEvent",paramString);
method.setAccessible(true);
method.invoke(theQueue, event);
}
}
catch (MacMessageException mme) {
throw mme;
}
catch (Throwable e) {
LOG.error(e);
}
}
while (isBlockedDocumentRoot(documentRoot, messageNumber));
}
private static boolean isBlockedDocumentRoot(Window documentRoot, Integer messageNumber) {
synchronized (lock) {
return messageNumber.equals(blockedDocumentRoots.get(documentRoot));
}
}
private static Window findDocumentRoot (final Component c) {
if (c == null) return null;
Window w = c instanceof Window ? (Window)c : getContainingWindow(c);
synchronized (c.getTreeLock()) {
while (w.getOwner() != null) {
w = w.getOwner();
}
}
return w;
}
// This method is not available in jdk 1.6.0_6. Should be changed to the JDK implementation
// as soon as we will have switched on JDK 7.
private static Window getContainingWindow(Component comp) {
while (comp != null && !(comp instanceof Window)) {
comp = comp.getParent();
}
return (Window)comp;
}
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);
} else {
blockedDocumentRoots.put(w, 1);
}
}
pumpEventsDocumentExclusively(w);
synchronized (lock) {
windowFromId.remove(windowPtr);
}
}
private enum COMMON_DIALOG_PARAM_TYPE {
title,
message,
errorStyle,
doNotAskDialogOption1,
doNotAskDialogOption2,
nativeFocusedWindow
}
private enum MESSAGE_DIALOG_PARAM_TYPE {
buttonsArray,
defaultOptionIndex,
focusedOptionIndex
}
private enum ALERT_DIALOG_PARAM_TYPE {
defaultText,
alternateText,
otherText
}
private static class DialogParamsWrapper {
private ID window = null;
private final Map<Enum, Object> params;
private final DialogType dialogType;
private enum DialogType {
alert,
message
}
private DialogParamsWrapper(@NotNull DialogType t, @NotNull Map<Enum, Object> p) {
dialogType = t;
params = p;
}
private void setNativeWindow (final ID w) {
window = w;
}
private ID getParamsAsID() {
if (window == null) {
throw new MacMessageException("Window should be in the list.");
}
params.put(COMMON_DIALOG_PARAM_TYPE.nativeFocusedWindow, window);
ID paramsAsID = null;
switch (dialogType) {
case alert:
paramsAsID = getParamsForAlertDialog(params);
break;
case message:
paramsAsID = getParamsForMessageDialog(params);
break;
}
return paramsAsID;
}
private static ID getParamsForAlertDialog(@NotNull Map<Enum, Object> params) {
return invoke("NSArray", "arrayWithObjects:",
params.get(COMMON_DIALOG_PARAM_TYPE.title),
params.get(ALERT_DIALOG_PARAM_TYPE.defaultText),
params.get(ALERT_DIALOG_PARAM_TYPE.alternateText),
params.get(ALERT_DIALOG_PARAM_TYPE.otherText),
params.get(COMMON_DIALOG_PARAM_TYPE.message),
params.get(COMMON_DIALOG_PARAM_TYPE.nativeFocusedWindow),
nsString(""),
params.get(COMMON_DIALOG_PARAM_TYPE.errorStyle),
params.get(COMMON_DIALOG_PARAM_TYPE.doNotAskDialogOption1),
params.get(COMMON_DIALOG_PARAM_TYPE.doNotAskDialogOption2),
null);
}
private static ID getParamsForMessageDialog(@NotNull Map<Enum, Object> params) {
return invoke("NSArray", "arrayWithObjects:",
params.get(COMMON_DIALOG_PARAM_TYPE.title),
params.get(COMMON_DIALOG_PARAM_TYPE.message),
params.get(COMMON_DIALOG_PARAM_TYPE.nativeFocusedWindow),
nsString(""),
params.get(COMMON_DIALOG_PARAM_TYPE.errorStyle),
params.get(COMMON_DIALOG_PARAM_TYPE.doNotAskDialogOption1),
params.get(MESSAGE_DIALOG_PARAM_TYPE.defaultOptionIndex),
params.get(MESSAGE_DIALOG_PARAM_TYPE.focusedOptionIndex),
params.get(MESSAGE_DIALOG_PARAM_TYPE.buttonsArray),
params.get(COMMON_DIALOG_PARAM_TYPE.doNotAskDialogOption2),
null);
}
}
@Messages.YesNoCancelResult
public static int showAlertDialog(@NotNull String title,
@NotNull String defaultText,
@Nullable final String alternateText,
@Nullable final String otherText,
final String message,
@Nullable Window window,
final boolean errorStyle,
@Nullable final DialogWrapper.DoNotAskOption doNotAskDialogOption) {
Map<Enum, Object> params = new HashMap<Enum, Object> ();
ID pool = invoke(invoke("NSAutoreleasePool", "alloc"), "init");
try {
params.put(COMMON_DIALOG_PARAM_TYPE.title, nsString(title));
params.put(ALERT_DIALOG_PARAM_TYPE.defaultText, nsString(UIUtil.removeMnemonic(defaultText)));
params.put(ALERT_DIALOG_PARAM_TYPE.alternateText, nsString(otherText == null ? "-1" : UIUtil.removeMnemonic(otherText)));
params.put(ALERT_DIALOG_PARAM_TYPE.otherText, nsString(alternateText == null ? "-1" : UIUtil.removeMnemonic(alternateText)));
// replace % -> %% to avoid formatted parameters (causes SIGTERM)
params.put(COMMON_DIALOG_PARAM_TYPE.message, nsString(StringUtil.stripHtml(message == null ? "" : message, true).replace("%", "%%")));
params.put(COMMON_DIALOG_PARAM_TYPE.errorStyle, nsString(errorStyle ? "error" : "-1"));
params.put(COMMON_DIALOG_PARAM_TYPE.doNotAskDialogOption1, nsString(doNotAskDialogOption == null || !doNotAskDialogOption.canBeHidden()
// TODO: state=!doNotAsk.shouldBeShown()
? "-1"
: doNotAskDialogOption.getDoNotShowMessage()));
params.put(COMMON_DIALOG_PARAM_TYPE.doNotAskDialogOption2, nsString(doNotAskDialogOption != null
&& !doNotAskDialogOption.isToBeShown() ? "checked" : "-1"));
MessageResult result = resultsFromDocumentRoot.remove(
showDialog(window, "showSheet:", new DialogParamsWrapper(DialogParamsWrapper.DialogType.alert, params)));
int convertedResult = convertReturnCodeFromNativeAlertDialog(result.myReturnCode, alternateText);
if (doNotAskDialogOption != null && doNotAskDialogOption.canBeHidden()) {
boolean operationCanceled = convertedResult == Messages.CANCEL;
if (!operationCanceled || doNotAskDialogOption.shouldSaveOptionsOnCancel()) {
doNotAskDialogOption.setToBeShown(!result.mySuppress, convertedResult);
}
}
return convertedResult;
}
finally {
invoke(pool, "release");
}
}
@Override
public int showMessageDialog(@NotNull final String title,
final String message,
@NotNull final String[] buttons,
final boolean errorStyle,
@Nullable Window window,
final int defaultOptionIndex,
final int focusedOptionIndex,
@Nullable final DialogWrapper.DoNotAskOption doNotAskDialogOption) {
ID pool = invoke(invoke("NSAutoreleasePool", "alloc"), "init");
try {
final ID buttonsArray = invoke("NSMutableArray", "array");
for (String s : buttons) {
ID s1 = nsString(UIUtil.removeMnemonic(s));
invoke(buttonsArray, "addObject:", s1);
}
Map<Enum, Object> params = new HashMap<Enum, Object>();
params.put(COMMON_DIALOG_PARAM_TYPE.title, nsString(title));
// replace % -> %% to avoid formatted parameters (causes SIGTERM)
params.put(COMMON_DIALOG_PARAM_TYPE.message, nsString(StringUtil.stripHtml(message == null ? "" : message, true).replace("%", "%%")));
params.put(COMMON_DIALOG_PARAM_TYPE.errorStyle, nsString(errorStyle ? "error" : "-1"));
params.put(COMMON_DIALOG_PARAM_TYPE.doNotAskDialogOption1, nsString(doNotAskDialogOption == null || !doNotAskDialogOption.canBeHidden()
// TODO: state=!doNotAsk.shouldBeShown()
? "-1"
: doNotAskDialogOption.getDoNotShowMessage()));
params.put(COMMON_DIALOG_PARAM_TYPE.doNotAskDialogOption2, nsString(doNotAskDialogOption != null && !doNotAskDialogOption.isToBeShown() ? "checked" : "-1"));
params.put(MESSAGE_DIALOG_PARAM_TYPE.defaultOptionIndex, nsString(Integer.toString(defaultOptionIndex)));
params.put(MESSAGE_DIALOG_PARAM_TYPE.focusedOptionIndex, nsString(Integer.toString(focusedOptionIndex)));
params.put(MESSAGE_DIALOG_PARAM_TYPE.buttonsArray, buttonsArray);
MessageResult result = resultsFromDocumentRoot.remove(showDialog(window, "showVariableButtonsSheet:",
new DialogParamsWrapper(DialogParamsWrapper.DialogType.message, params)));
final int code = convertReturnCodeFromNativeMessageDialog(result.myReturnCode);
final int cancelCode = buttons.length - 1;
if (doNotAskDialogOption != null && doNotAskDialogOption.canBeHidden()) {
if (cancelCode != code || doNotAskDialogOption.shouldSaveOptionsOnCancel()) {
doNotAskDialogOption.setToBeShown(!result.mySuppress, code);
}
}
return code;
}
finally {
invoke(pool, "release");
}
}
//title, message, errorStyle, window, paramsArray, doNotAskDialogOption, "showVariableButtonsSheet:"
private static Window showDialog(@Nullable Window window, final String methodName, final DialogParamsWrapper paramsWrapper) {
final Window foremostWindow = getForemostWindow(window);
final Window documentRoot = getDocumentRootFromWindow(foremostWindow);
final ID nativeFocusedWindow = MacUtil.findWindowFromJavaWindow(foremostWindow);
paramsWrapper.setNativeWindow(nativeFocusedWindow);
final ID paramsArray = paramsWrapper.getParamsAsID();
foremostWindow.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
super.windowClosed(e);
//if (blockedDocumentRoots.get(documentRoot) != null) {
// LOG.assertTrue(blockedDocumentRoots.get(documentRoot) < 2);
//}
queuesFromDocumentRoot.remove(documentRoot);
if (blockedDocumentRoots.remove(documentRoot) != null) {
throw new MacMessageException("Owner window has been removed");
}
}
});
final ID delegate = invoke(invoke(getObjcClass("NSAlertDelegate_"), "alloc"), "init");
IdeFocusManager.getGlobalInstance().setTypeaheadEnabled(false);
runOrPostponeForWindow(documentRoot, new Runnable() {
@Override
public void run() {
invoke(delegate, "performSelectorOnMainThread:withObject:waitUntilDone:",
createSelector(methodName), paramsArray, false);
}
});
startModal(documentRoot, nativeFocusedWindow);
IdeFocusManager.getGlobalInstance().setTypeaheadEnabled(true);
return documentRoot;
}
private static int convertReturnCodeFromNativeMessageDialog(int result) {
return result - 1000;
}
@Messages.YesNoCancelResult
private static int convertReturnCodeFromNativeAlertDialog(int returnCode, String alternateText) {
// DEFAULT = 1
// ALTERNATE = 0
// OTHER = -1 (cancel)
int cancelCode;
int code;
if (alternateText != null) {
// DEFAULT = 0
// ALTERNATE = 1
// CANCEL = 2
cancelCode = Messages.CANCEL;
switch (returnCode) {
case 1:
code = Messages.YES;
break;
case 0:
code = Messages.NO;
break;
case -1: // cancel
default:
code = Messages.CANCEL;
break;
}
}
else {
// DEFAULT = 0
// CANCEL = 1
cancelCode = 1;
switch (returnCode) {
case 1:
code = Messages.YES;
break;
case -1: // cancel
default:
code = Messages.NO;
break;
}
}
if (cancelCode == code) {
code = Messages.CANCEL;
}
LOG.assertTrue(code == Messages.YES || code == Messages.NO || code == Messages.CANCEL, code);
return code;
}
private static void runOrPostponeForWindow(Window documentRoot, Runnable task) {
synchronized (lock) {
MacMessagesQueue<Runnable> queue = queuesFromDocumentRoot.get(documentRoot);
if (queue == null) {
queue = new MacMessagesQueue<Runnable>();
queuesFromDocumentRoot.put(documentRoot, queue);
}
queue.runOrEnqueue(task);
}
}
private static Window getForemostWindow(final Window window) {
Window _window = null;
IdeFocusManager ideFocusManager = IdeFocusManager.getGlobalInstance();
Component focusOwner = IdeFocusManager.findInstance().getFocusOwner();
// Let's ask for a focused component first
if (focusOwner != null) {
_window = SwingUtilities.getWindowAncestor(focusOwner);
}
if (_window == null) {
// Looks like ide lost focus, let's ask about the last focused component
focusOwner = ideFocusManager.getLastFocusedFor(ideFocusManager.getLastFocusedFrame());
if (focusOwner != null) {
_window = SwingUtilities.getWindowAncestor(focusOwner);
}
}
if (_window == null) {
_window = WindowManager.getInstance().findVisibleFrame();
}
if (_window == null && window != null) {
// It might be we just has not opened a frame yet.
// So let's ask AWT
focusOwner = window.getMostRecentFocusOwner();
if (focusOwner != null) {
_window = SwingUtilities.getWindowAncestor(focusOwner);
}
}
if (_window != null) {
// We have successfully found the window
// Let's check that we have not missed a blocker
if (ModalityHelper.isModalBlocked(_window)) {
_window = ModalityHelper.getModalBlockerFor(_window);
}
}
if (SystemInfo.isAppleJvm && MacUtil.getWindowTitle(_window) == null) {
// With Apple JDK we cannot find a window if it does not have a title
// Let's show a dialog instead of the message.
throw new MacMessageException("MacMessage parent does not have a title.");
}
while (_window != null && MacUtil.getWindowTitle(_window) == null) {
_window = _window.getOwner();
//At least our frame should have a title
}
while (Registry.is("skip.untitled.windows.for.mac.messages") && _window != null && _window instanceof JDialog && !((JDialog)_window).isModal()) {
_window = _window.getOwner();
}
return _window;
}
/**
* Document root is intended to queue messages per a document root
*/
private static Window getDocumentRootFromWindow(Window window) {
return findDocumentRoot(window);
}
@Messages.YesNoCancelResult
private static int showAlertDialog(@NotNull String title,
@NotNull String okText,
@Nullable String alternateText,
@Nullable String cancelText,
String message,
@Nullable Window window) {
return showAlertDialog(title, okText, alternateText, cancelText, message, window, false, null);
}
}