From fcae53df4cca9f7dd0baa2bcaf77c59ef3380e59 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Thu, 7 Apr 2011 16:30:21 +0400 Subject: [PATCH] [mac] show/hide toolbar native mac window decoration button --- .../openapi/wm/impl/IdeFrameImpl.java | 14 +- .../openapi/wm/impl/WindowManagerImpl.java | 5 + .../ui/mac/MacFileChooserDialogImpl.java | 24 +-- .../ui/mac/MacMainFrameDecorator.java | 179 ++++++++++++++++++ .../ui/mac/foundation/Foundation.java | 4 + 5 files changed, 200 insertions(+), 26 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/ui/mac/MacMainFrameDecorator.java diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java index a06a1f455bbf..f73c690b92c5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java @@ -49,6 +49,8 @@ import com.intellij.openapi.wm.impl.status.ToggleReadOnlyAttributePanel; import com.intellij.ui.AppUIUtil; import com.intellij.ui.BalloonLayout; import com.intellij.ui.FocusTrackback; +import com.intellij.ui.mac.MacMainFrameDecorator; +import com.intellij.util.PlatformUtils; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -58,9 +60,6 @@ import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; /** * @author Anton Katilin @@ -80,6 +79,7 @@ public class IdeFrameImpl extends JFrame implements IdeFrame, DataProvider { private IdeRootPane myRootPane; private final BalloonLayout myBalloonLayout; private static boolean myUpdatingTitle; + private MacMainFrameDecorator myFrameDecorator; public IdeFrameImpl(ApplicationInfoEx applicationInfoEx, ActionManagerEx actionManager, UISettings uiSettings, DataManager dataManager, final Application application, final String[] commandLineArgs) { @@ -130,6 +130,10 @@ public class IdeFrameImpl extends JFrame implements IdeFrame, DataProvider { setFocusableWindowState(true); } }); + + if (SystemInfo.isMac && myFrameDecorator == null) { + myFrameDecorator = new MacMainFrameDecorator(this, PlatformUtils.isCidr()); + } } /** @@ -337,6 +341,10 @@ public class IdeFrameImpl extends JFrame implements IdeFrame, DataProvider { myRootPane = null; } + if (myFrameDecorator != null) { + myFrameDecorator.remove(); + } + FocusTrackback.release(this); super.dispose(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java index 2157bcd4ed71..651451d8e77e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java @@ -552,6 +552,11 @@ public final class WindowManagerImpl extends WindowManagerEx implements Applicat myProject2Frame.remove(project); if (myProject2Frame.isEmpty()) { myProject2Frame.put(null, frame); + + if (ApplicationManager.getApplication().isDisposeInProgress()) { + // disposing last frame if quitting + frame.dispose(); + } } else { Disposer.dispose((StatusBarEx) frame.getStatusBar()); diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java index fbb40eecfd1e..effadf8b1a61 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java @@ -138,30 +138,8 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog { } else if (activeWindow instanceof JDialog) { activeWindowTitle = ((JDialog)activeWindow).getTitle(); } - if (activeWindowTitle == null || activeWindowTitle.length() == 0) return; - - final ID sharedApplication = invoke("NSApplication", "sharedApplication"); - final ID windows = invoke(sharedApplication, "windows"); - final ID windowEnumerator = invoke(windows, "objectEnumerator"); - - ID focusedWindow = null; - 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 = Foundation.toStringViaUTF8(windowTitle); - if (titleString.equals(activeWindowTitle)) { - if (1 == invoke(window, "isVisible").intValue()) { - focusedWindow = window; - break; - } - } - } - } + final ID focusedWindow = MacMainFrameDecorator.findWindowForTitle(activeWindowTitle); if (focusedWindow != null) { invoke(chooser, "beginSheetForDirectory:file:types:modalForWindow:modalDelegate:didEndSelector:contextInfo:", directory, file, null, focusedWindow, self, Foundation.createSelector("openPanelDidEnd:returnCode:contextInfo:"), null); diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacMainFrameDecorator.java b/platform/platform-impl/src/com/intellij/ui/mac/MacMainFrameDecorator.java new file mode 100644 index 000000000000..4327d36a5ba7 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacMainFrameDecorator.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2011 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.ide.ui.UISettings; +import com.intellij.ide.ui.UISettingsListener; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.mac.foundation.Foundation; +import com.intellij.ui.mac.foundation.ID; +import com.intellij.util.Function; +import com.sun.jna.Callback; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.intellij.ui.mac.foundation.Foundation.invoke; +import static com.intellij.ui.mac.foundation.Foundation.toStringViaUTF8; + +/** + * User: spLeaner + */ +public class MacMainFrameDecorator implements UISettingsListener, Disposable { + private static boolean SHOWN = false; + + private static Callback SET_VISIBLE_CALLBACK = new Callback() { + public void callback(ID caller, ID selector, ID value) { + SHOWN = value.intValue() == 1; + SwingUtilities.invokeLater(CURRENT_SETTER); + } + }; + + private static Callback IS_VISIBLE = new Callback() { + public boolean callback(ID caller) { + return SHOWN; + } + }; + + private static AtomicInteger UNIQUE_COUNTER = new AtomicInteger(0); + + public static final Runnable TOOLBAR_SETTER = new Runnable() { + @Override + public void run() { + final UISettings settings = UISettings.getInstance(); + settings.SHOW_MAIN_TOOLBAR = SHOWN; + settings.fireUISettingsChanged(); + } + }; + + public static final Runnable NAVBAR_SETTER = new Runnable() { + @Override + public void run() { + final UISettings settings = UISettings.getInstance(); + settings.SHOW_NAVIGATION_BAR = SHOWN; + settings.fireUISettingsChanged(); + } + }; + + public static final Function NAVBAR_GETTER = new Function() { + @Override + public Boolean fun(Object o) { + return UISettings.getInstance().SHOW_NAVIGATION_BAR; + } + }; + + public static final Function TOOLBAR_GETTER = new Function() { + @Override + public Boolean fun(Object o) { + return UISettings.getInstance().SHOW_MAIN_TOOLBAR; + } + }; + + private static Runnable CURRENT_SETTER = null; + private static Function CURRENT_GETTER = null; + private String myClassName; + + public MacMainFrameDecorator(@NotNull final Frame frame, final boolean navBar) { + final ID window = findWindowForTitle(frame.getTitle()); + if (window == null) return; + + if (CURRENT_SETTER == null) { + CURRENT_SETTER = navBar ? NAVBAR_SETTER : TOOLBAR_SETTER; + CURRENT_GETTER = navBar ? NAVBAR_GETTER : TOOLBAR_GETTER; + SHOWN = CURRENT_GETTER.fun(null); + } + + UISettings.getInstance().addUISettingsListener(this, this); + + final ID pool = invoke("NSAutoreleasePool", "new"); + + try { + myClassName = "IdeaToolbar" + UNIQUE_COUNTER.incrementAndGet(); + + final ID ownToolbar = Foundation.registerObjcClass(Foundation.getClass("NSToolbar"), myClassName); + Foundation.registerObjcClassPair(ownToolbar); + + ID toolbar = invoke(invoke(myClassName, "alloc"), "initWithIdentifier:", Foundation.cfString(myClassName)); + Foundation.cfRetain(toolbar); + + invoke(toolbar, "setVisible:", 0); // hide native toolbar by default + + Foundation.addMethod(ownToolbar, Foundation.createSelector("setVisible:"), SET_VISIBLE_CALLBACK, "v*"); + Foundation.addMethod(ownToolbar, Foundation.createSelector("isVisible"), IS_VISIBLE, "B*"); + + invoke(window, "setToolbar:", toolbar); + invoke(window, "setShowsToolbarButton:", 1); + } + finally { + invoke(pool, "release"); + } + } + + public void remove() { + // TODO: clean up? + Disposer.dispose(this); + } + + @Override + public void uiSettingsChanged(final UISettings source) { + if (CURRENT_GETTER != null) { + SHOWN = CURRENT_GETTER.fun(null); + } + } + + @Override + public void dispose() { + } + + @Nullable + public static ID findWindowForTitle(final String title) { + if (title == null || title.length() == 0) 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 (titleString.equals(title)) { + if (1 == invoke(window, "isVisible").intValue()) { + focusedWindow = window; + break; + } + } + } + } + } + finally { + invoke(pool, "release"); + } + + return focusedWindow; + } +} diff --git a/platform/util/src/com/intellij/ui/mac/foundation/Foundation.java b/platform/util/src/com/intellij/ui/mac/foundation/Foundation.java index 227f9fd3b0f4..85dd878aa3f5 100644 --- a/platform/util/src/com/intellij/ui/mac/foundation/Foundation.java +++ b/platform/util/src/com/intellij/ui/mac/foundation/Foundation.java @@ -67,6 +67,10 @@ public class Foundation { return invoke(getClass(cls), createSelector(selector), args); } + public static ID invoke(final ID id, final String selector, Object... args) { + return invoke(id, createSelector(selector), args); + } + public static ID registerObjcClass(ID superCls, String name) { return myFoundationLibrary.objc_allocateClassPair(superCls, name, 0); }