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 8ae58511780b..891008c295bf 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 @@ -19,6 +19,7 @@ import com.intellij.ide.AppLifecycleListener; import com.intellij.ide.DataManager; import com.intellij.ide.impl.ProjectUtil; import com.intellij.ide.ui.UISettings; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.notification.impl.IdeNotificationArea; import com.intellij.openapi.Disposable; import com.intellij.openapi.MnemonicHelper; @@ -69,6 +70,7 @@ import java.io.File; // Made non-final for Fabrique public class IdeFrameImpl extends JFrame implements IdeFrame, DataProvider { + private static final String FULL_SCREEN = "FullScreen"; private String myTitle; private String myFileTitle; @@ -81,6 +83,8 @@ public class IdeFrameImpl extends JFrame implements IdeFrame, DataProvider { private final BalloonLayout myBalloonLayout; private static boolean myUpdatingTitle; private MacMainFrameDecorator myFrameDecorator; + + private boolean myRestoreFullscreen; public IdeFrameImpl(ApplicationInfoEx applicationInfoEx, ActionManagerEx actionManager, UISettings uiSettings, DataManager dataManager, final Application application, final String[] commandLineArgs) { @@ -275,6 +279,14 @@ public class IdeFrameImpl extends JFrame implements IdeFrame, DataProvider { } public void setProject(final Project project) { + if (SystemInfo.isMacOSLion && myProject != project && project != null) { + myRestoreFullscreen = myProject == null && shouldRestoreFullScreen(project); + + if (myProject != null) { + storeFullScreenStateIfNeeded(false); // disable for old project + } + } + myProject = project; if (project != null) { if (myRootPane != null) { @@ -293,6 +305,28 @@ public class IdeFrameImpl extends JFrame implements IdeFrame, DataProvider { if (project == null) { FocusTrackback.release(this); } + + if (isVisible() && myRestoreFullscreen) { + MacMainFrameDecorator.toggleFullScreen(this); + myRestoreFullscreen = false; + storeFullScreenStateIfNeeded(false); // reset + } + } + + @Override + public void setVisible(boolean b) { + super.setVisible(b); + + if (b && myRestoreFullscreen) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + MacMainFrameDecorator.toggleFullScreen(IdeFrameImpl.this); + myRestoreFullscreen = false; + storeFullScreenStateIfNeeded(false); // reset + } + }); + } } private void installDefaultProjectStatusBarWidgets(@NotNull final Project project) { @@ -352,6 +386,30 @@ public class IdeFrameImpl extends JFrame implements IdeFrame, DataProvider { super.dispose(); } + public void storeFullScreenStateIfNeeded() { + storeFullScreenStateIfNeeded(MacMainFrameDecorator.isFullScreenMode(this)); + } + + public void storeFullScreenStateIfNeeded(boolean state) { + if (!SystemInfo.isMacOSLion) return; + + if (myProject != null) { + PropertiesComponent.getInstance(myProject).setValue(FULL_SCREEN, Boolean.valueOf(state).toString()); + } else { + //PropertiesComponent.getInstance().setValue(FULL_SCREEN, Boolean.valueOf(state).toString()); + } + } + + public static boolean shouldRestoreFullScreen(Project project) { + if (!SystemInfo.isMacOSLion) return false; + + if (project != null) { + return PropertiesComponent.getInstance(project).getBoolean(FULL_SCREEN, false); + } else { + return false; // PropertiesComponent.getInstance().getBoolean(FULL_SCREEN, false); + } + } + @Override public void paint(Graphics g) { UIUtil.applyRenderingHints(g); 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 1ca93839d9cc..4aaf890f81cf 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 @@ -16,6 +16,7 @@ package com.intellij.openapi.wm.impl; import com.intellij.Patches; +import com.intellij.ide.AppLifecycleListener; import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.impl.DataManagerImpl; @@ -43,6 +44,7 @@ import com.intellij.ui.mac.MacMainFrameDecorator; import com.intellij.util.Alarm; import com.intellij.util.ArrayUtil; import com.intellij.util.EventDispatcher; +import com.intellij.util.messages.MessageBus; import com.intellij.util.ui.UIUtil; import com.sun.jna.platform.WindowUtils; import org.jdom.Element; @@ -134,7 +136,8 @@ public final class WindowManagerImpl extends WindowManagerEx implements Applicat public WindowManagerImpl(DataManager dataManager, ApplicationInfoEx applicationInfoEx, ActionManagerEx actionManager, - UISettings uiSettings) { + UISettings uiSettings, + MessageBus bus) { myApplicationInfoEx = applicationInfoEx; myDataManager = dataManager; myActionManager = actionManager; @@ -185,6 +188,26 @@ public final class WindowManagerImpl extends WindowManagerEx implements Applicat } } }; + + bus.connect().subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() { + @Override + public void appClosing() { + // save fullscreen window states + if (SystemInfo.isMacOSLion) { + Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); + + if (openProjects.length > 0) { + WindowManagerEx wm = WindowManagerEx.getInstanceEx(); + for (Project project : openProjects) { + IdeFrameImpl frame = wm.getFrame(project); + if (frame != null) { + frame.storeFullScreenStateIfNeeded(); + } + } + } + } + } + }); } public void showFrame(final String[] args) { @@ -672,7 +695,8 @@ public final class WindowManagerImpl extends WindowManagerEx implements Applicat final IdeFrameImpl frame = getFrame(project); if (frame != null) { - final Rectangle rectangle = frame.getBounds(); + boolean usePreviousBounds = SystemInfo.isMacOSLion && WindowManagerEx.getInstanceEx().isFullScreen(frame); + final Rectangle rectangle = usePreviousBounds ? myFrameBounds : frame.getBounds(); frameElement.setAttribute(X_ATTR, Integer.toString(rectangle.x)); frameElement.setAttribute(Y_ATTR, Integer.toString(rectangle.y)); frameElement.setAttribute(WIDTH_ATTR, Integer.toString(rectangle.width)); diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacMainFrameDecorator.java b/platform/platform-impl/src/com/intellij/ui/mac/MacMainFrameDecorator.java index 58230fc2c45a..f6843b23bdc7 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/MacMainFrameDecorator.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacMainFrameDecorator.java @@ -20,7 +20,7 @@ import com.intellij.ide.ui.UISettingsListener; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.wm.impl.IdeFrameImpl; import com.intellij.ui.mac.foundation.Foundation; import com.intellij.ui.mac.foundation.ID; import com.intellij.util.Function; @@ -90,7 +90,9 @@ public class MacMainFrameDecorator implements UISettingsListener, Disposable { private static Runnable CURRENT_SETTER = null; private static Function CURRENT_GETTER = null; - private String myClassName; + + private Callback myDidExit; + private Callback myDidEnter; public MacMainFrameDecorator(@NotNull final Frame frame, final boolean navBar) { final ID window = findWindowForTitle(frame.getTitle()); @@ -106,20 +108,75 @@ public class MacMainFrameDecorator implements UISettingsListener, Disposable { final ID pool = invoke("NSAutoreleasePool", "new"); + int v = UNIQUE_COUNTER.incrementAndGet(); + try { if (SystemInfo.isMacOSLion) { - // fullscreen + // fullscreen support + + final ID delegateClass = Foundation.registerObjcClass(Foundation.getClass("NSObject"), "IdeaNSWindowDelegate" + v); + Foundation.registerObjcClassPair(delegateClass); + myDidExit = new Callback() { + public void callback(ID caller, ID notification) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (frame instanceof IdeFrameImpl) { + ((IdeFrameImpl)frame).storeFullScreenStateIfNeeded(false); + } + } + }); + } + }; + Foundation.addMethod(delegateClass, Foundation.createSelector("windowDidExitFullScreen:"), myDidExit, "v*"); + + myDidEnter = new Callback() { + public void callback(ID caller, ID notification) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (frame instanceof IdeFrameImpl) { + //((IdeFrameImpl)frame).storeFullScreenStateIfNeeded(true); + + // fix problem with bottom empty bar + // it seems like the title is still visible in fullscreen but the window itself shifted up for titlebar height + // and the size of the frame is still calculated to be the height of the screen which is wrong + // so just add these titlebar height to the frame height once again + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + frame.setSize(frame.getWidth(), frame.getHeight() + frame.getInsets().top); + } + }); + } + } + }); + } + }; + + Foundation.addMethod(delegateClass, Foundation.createSelector("windowDidEnterFullScreen:"), myDidEnter, "v*"); + + // enable fullscreen titlebar button invoke(window, "setCollectionBehavior:", 1 << 7); // NSCollectionBehaviorFullScreenPrimary = 1 << 7, NSCollectionBehaviorFullScreenAuxiliary = 1 << 8 + + ID notificationCenter = invoke("NSNotificationCenter", "defaultCenter"); + + ID delegate = invoke(invoke("IdeaNSWindowDelegate" + v, "alloc"), "init"); + invoke(notificationCenter, "addObserver:selector:name:object:", delegate, + Foundation.createSelector("windowDidEnterFullScreen:"), + Foundation.cfString("NSWindowDidEnterFullScreenNotification"), window); + + //invoke(notificationCenter, "addObserver:selector:name:object:", delegate, + // Foundation.createSelector("windowDidExitFullScreen:"), + // Foundation.cfString("NSWindowDidExitFullScreenNotification"), window); } else { // toggle toolbar - - myClassName = "IdeaToolbar" + UNIQUE_COUNTER.incrementAndGet(); - - final ID ownToolbar = Foundation.registerObjcClass(Foundation.getClass("NSToolbar"), myClassName); + String className = "IdeaToolbar" + v; + final ID ownToolbar = Foundation.registerObjcClass(Foundation.getClass("NSToolbar"), className); Foundation.registerObjcClassPair(ownToolbar); - ID toolbar = invoke(invoke(myClassName, "alloc"), "initWithIdentifier:", Foundation.cfString(myClassName)); + ID toolbar = invoke(invoke(className, "alloc"), "initWithIdentifier:", Foundation.cfString(className)); Foundation.cfRetain(toolbar); invoke(toolbar, "setVisible:", 0); // hide native toolbar by default @@ -152,6 +209,15 @@ public class MacMainFrameDecorator implements UISettingsListener, Disposable { public void dispose() { } + public static void toggleFullScreen(Frame frame) { + if (!SystemInfo.isMacOSLion) return; + + final ID window = findWindowForTitle(frame.getTitle()); + if (window == null) return; + + invoke(window, "toggleFullScreen:", window); + } + public static boolean isFullScreenMode(@NotNull Frame frame) { if (!SystemInfo.isMacOSLion) return false;