| /* |
| * 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.openapi.wm.impl; |
| |
| import com.intellij.Patches; |
| import com.intellij.ide.AppLifecycleListener; |
| import com.intellij.ide.DataManager; |
| import com.intellij.ide.GeneralSettings; |
| import com.intellij.ide.impl.DataManagerImpl; |
| import com.intellij.ide.ui.UISettings; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.actionSystem.ex.ActionManagerEx; |
| import com.intellij.openapi.application.Application; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ex.ApplicationInfoEx; |
| import com.intellij.openapi.components.*; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectManager; |
| import com.intellij.openapi.ui.popup.JBPopup; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.openapi.wm.IdeFrame; |
| import com.intellij.openapi.wm.StatusBar; |
| import com.intellij.openapi.wm.WindowManagerListener; |
| import com.intellij.openapi.wm.ex.WindowManagerEx; |
| import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame; |
| import com.intellij.ui.ScreenUtil; |
| import com.intellij.util.Alarm; |
| 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; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.awt.peer.ComponentPeer; |
| import java.awt.peer.FramePeer; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * @author Anton Katilin |
| * @author Vladimir Kondratyev |
| */ |
| @State( |
| name = "WindowManager", |
| storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/window.manager.xml")}) |
| public final class WindowManagerImpl extends WindowManagerEx implements NamedComponent, PersistentStateComponent<Element> { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.WindowManagerImpl"); |
| |
| @NonNls public static final String FULL_SCREEN = "ide.frame.full.screen"; |
| |
| @NonNls private static final String FOCUSED_WINDOW_PROPERTY_NAME = "focusedWindow"; |
| @NonNls private static final String X_ATTR = "x"; |
| @NonNls private static final String FRAME_ELEMENT = "frame"; |
| @NonNls private static final String Y_ATTR = "y"; |
| @NonNls private static final String WIDTH_ATTR = "width"; |
| @NonNls private static final String HEIGHT_ATTR = "height"; |
| @NonNls private static final String EXTENDED_STATE_ATTR = "extended-state"; |
| |
| static { |
| try { |
| System.loadLibrary("jawt"); |
| } |
| catch (Throwable t) { |
| LOG.info("jawt failed to load", t); |
| } |
| } |
| |
| private static final boolean ORACLE_BUG_8007219 = SystemInfo.isMac && SystemInfo.isJavaVersionAtLeast("1.7"); |
| private static final int ORACLE_BUG_8007219_THRESHOLD = 5; |
| |
| private Boolean myAlphaModeSupported = null; |
| |
| private final EventDispatcher<WindowManagerListener> myEventDispatcher = EventDispatcher.create(WindowManagerListener.class); |
| |
| private final CommandProcessor myCommandProcessor; |
| private final WindowWatcher myWindowWatcher; |
| /** |
| * That is the default layout. |
| */ |
| private final DesktopLayout myLayout; |
| |
| private final HashMap<Project, IdeFrameImpl> myProject2Frame; |
| |
| private final HashMap<Project, Set<JDialog>> myDialogsToDispose; |
| |
| /** |
| * This members is needed to read frame's bounds from XML. |
| * <code>myFrameBounds</code> can be <code>null</code>. |
| */ |
| private Rectangle myFrameBounds; |
| private int myFrameExtendedState; |
| private final WindowAdapter myActivationListener; |
| private final ApplicationInfoEx myApplicationInfoEx; |
| private final DataManager myDataManager; |
| private final ActionManagerEx myActionManager; |
| private final UISettings myUiSettings; |
| |
| /** |
| * invoked by reflection |
| */ |
| public WindowManagerImpl(DataManager dataManager, |
| ApplicationInfoEx applicationInfoEx, |
| ActionManagerEx actionManager, |
| UISettings uiSettings, |
| MessageBus bus) { |
| myApplicationInfoEx = applicationInfoEx; |
| myDataManager = dataManager; |
| myActionManager = actionManager; |
| myUiSettings = uiSettings; |
| if (myDataManager instanceof DataManagerImpl) { |
| ((DataManagerImpl)myDataManager).setWindowManager(this); |
| } |
| |
| final Application application = ApplicationManager.getApplication(); |
| if (!application.isUnitTestMode()) { |
| Disposer.register(application, new Disposable() { |
| @Override |
| public void dispose() { |
| disposeRootFrame(); |
| } |
| }); |
| } |
| |
| myCommandProcessor = new CommandProcessor(); |
| myWindowWatcher = new WindowWatcher(); |
| final KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); |
| keyboardFocusManager.addPropertyChangeListener(FOCUSED_WINDOW_PROPERTY_NAME, myWindowWatcher); |
| if (Patches.SUN_BUG_ID_4218084) { |
| keyboardFocusManager.addPropertyChangeListener(FOCUSED_WINDOW_PROPERTY_NAME, new SUN_BUG_ID_4218084_Patch()); |
| } |
| myLayout = new DesktopLayout(); |
| myProject2Frame = new HashMap<Project, IdeFrameImpl>(); |
| myDialogsToDispose = new HashMap<Project, Set<JDialog>>(); |
| myFrameExtendedState = Frame.NORMAL; |
| |
| myActivationListener = new WindowAdapter() { |
| @Override |
| public void windowActivated(WindowEvent e) { |
| Window activeWindow = e.getWindow(); |
| if (activeWindow instanceof IdeFrameImpl) { // must be |
| proceedDialogDisposalQueue(((IdeFrameImpl)activeWindow).getProject()); |
| } |
| } |
| }; |
| |
| bus.connect().subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() { |
| @Override |
| public void appClosing() { |
| // save full screen window states |
| if (isFullScreenSupportedInCurrentOS() && GeneralSettings.getInstance().isReopenLastProject()) { |
| 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(); |
| } |
| } |
| } |
| } |
| } |
| }); |
| |
| if (UIUtil.hasLeakingAppleListeners()) { |
| UIUtil.addAwtListener(new AWTEventListener() { |
| @Override |
| public void eventDispatched(AWTEvent event) { |
| if (event.getID() == ContainerEvent.COMPONENT_ADDED) { |
| if (((ContainerEvent)event).getChild() instanceof JViewport) { |
| UIUtil.removeLeakingAppleListeners(); |
| } |
| } |
| } |
| }, AWTEvent.CONTAINER_EVENT_MASK, application); |
| } |
| } |
| |
| @Override |
| @NotNull |
| public IdeFrameImpl[] getAllProjectFrames() { |
| final Collection<IdeFrameImpl> ideFrames = myProject2Frame.values(); |
| return ideFrames.toArray(new IdeFrameImpl[ideFrames.size()]); |
| } |
| |
| @Override |
| public JFrame findVisibleFrame() { |
| IdeFrameImpl[] frames = getAllProjectFrames(); |
| return frames.length > 0 ? frames[0] : (JFrame)WelcomeFrame.getInstance(); |
| } |
| |
| @Override |
| public void addListener(final WindowManagerListener listener) { |
| myEventDispatcher.addListener(listener); |
| } |
| |
| @Override |
| public void removeListener(final WindowManagerListener listener) { |
| myEventDispatcher.removeListener(listener); |
| } |
| |
| @Override |
| public final Rectangle getScreenBounds() { |
| return ScreenUtil.getAllScreensShape().getBounds(); |
| } |
| |
| @Override |
| public Rectangle getScreenBounds(@NotNull Project project) { |
| final GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); |
| final Point onScreen = getFrame(project).getLocationOnScreen(); |
| final GraphicsDevice[] devices = environment.getScreenDevices(); |
| for (final GraphicsDevice device : devices) { |
| final Rectangle bounds = device.getDefaultConfiguration().getBounds(); |
| if (bounds.contains(onScreen)) { |
| return bounds; |
| } |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public final boolean isInsideScreenBounds(final int x, final int y, final int width) { |
| return ScreenUtil.getAllScreensShape().contains(x, y, width, 1); |
| } |
| |
| @Override |
| public final boolean isInsideScreenBounds(final int x, final int y) { |
| return ScreenUtil.getAllScreensShape().contains(x, y); |
| } |
| |
| @Override |
| public final boolean isAlphaModeSupported() { |
| if (myAlphaModeSupported == null) { |
| myAlphaModeSupported = calcAlphaModelSupported(); |
| } |
| return myAlphaModeSupported.booleanValue(); |
| } |
| |
| private static boolean calcAlphaModelSupported() { |
| if (AWTUtilitiesWrapper.isTranslucencyAPISupported()) { |
| return AWTUtilitiesWrapper.isTranslucencySupported(AWTUtilitiesWrapper.TRANSLUCENT); |
| } |
| try { |
| return WindowUtils.isWindowAlphaSupported(); |
| } |
| catch (Throwable e) { |
| return false; |
| } |
| } |
| |
| @Override |
| public final void setAlphaModeRatio(final Window window, final float ratio) { |
| if (!window.isDisplayable() || !window.isShowing()) { |
| throw new IllegalArgumentException("window must be displayable and showing. window=" + window); |
| } |
| if (ratio < 0.0f || ratio > 1.0f) { |
| throw new IllegalArgumentException("ratio must be in [0..1] range. ratio=" + ratio); |
| } |
| if (!isAlphaModeSupported() || !isAlphaModeEnabled(window)) { |
| return; |
| } |
| |
| |
| setAlphaMode(window, ratio); |
| } |
| |
| private static void setAlphaMode(Window window, float ratio) { |
| try { |
| if (SystemInfo.isMacOSLeopard) { |
| if (window instanceof JWindow) { |
| ((JWindow)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); |
| } else if (window instanceof JDialog) { |
| ((JDialog)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); |
| } else if (window instanceof JFrame) { |
| ((JFrame)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); |
| } |
| } |
| else if (AWTUtilitiesWrapper.isTranslucencySupported(AWTUtilitiesWrapper.TRANSLUCENT)) { |
| AWTUtilitiesWrapper.setWindowOpacity(window, 1.0f - ratio); |
| } |
| else { |
| WindowUtils.setWindowAlpha(window, 1.0f - ratio); |
| } |
| } |
| catch (Throwable e) { |
| LOG.debug(e); |
| } |
| } |
| |
| @Override |
| public void setWindowMask(final Window window, @Nullable final Shape mask) { |
| try { |
| if (AWTUtilitiesWrapper.isTranslucencySupported(AWTUtilitiesWrapper.PERPIXEL_TRANSPARENT)) { |
| AWTUtilitiesWrapper.setWindowShape(window, mask); |
| } |
| else { |
| WindowUtils.setWindowMask(window, mask); |
| } |
| } |
| catch (Throwable e) { |
| LOG.debug(e); |
| } |
| } |
| |
| @Override |
| public void setWindowShadow(Window window, WindowShadowMode mode) { |
| if (window instanceof JWindow) { |
| JRootPane root = ((JWindow)window).getRootPane(); |
| root.putClientProperty("Window.shadow", mode == WindowShadowMode.DISABLED ? Boolean.FALSE : Boolean.TRUE); |
| root.putClientProperty("Window.style", mode == WindowShadowMode.SMALL ? "small" : null); |
| } |
| } |
| |
| @Override |
| public void resetWindow(final Window window) { |
| try { |
| if (!isAlphaModeSupported()) return; |
| |
| setWindowMask(window, null); |
| setAlphaMode(window, 0f); |
| setWindowShadow(window, WindowShadowMode.NORMAL); |
| } |
| catch (Throwable e) { |
| LOG.debug(e); |
| } |
| } |
| |
| @Override |
| public final boolean isAlphaModeEnabled(final Window window) { |
| if (!window.isDisplayable() || !window.isShowing()) { |
| throw new IllegalArgumentException("window must be displayable and showing. window=" + window); |
| } |
| return isAlphaModeSupported(); |
| } |
| |
| @Override |
| public final void setAlphaModeEnabled(final Window window, final boolean state) { |
| if (!window.isDisplayable() || !window.isShowing()) { |
| throw new IllegalArgumentException("window must be displayable and showing. window=" + window); |
| } |
| } |
| |
| @Override |
| public void hideDialog(JDialog dialog, Project project) { |
| if (project == null) { |
| dialog.dispose(); |
| } |
| else { |
| IdeFrameImpl frame = getFrame(project); |
| if (frame.isActive()) { |
| dialog.dispose(); |
| } |
| else { |
| queueForDisposal(dialog, project); |
| dialog.setVisible(false); |
| } |
| } |
| } |
| |
| @Override |
| public void adjustContainerWindow(Component c, Dimension oldSize, Dimension newSize) { |
| if (c == null) return; |
| |
| Window wnd = SwingUtilities.getWindowAncestor(c); |
| |
| if (wnd instanceof JWindow) { |
| JBPopup popup = (JBPopup)((JWindow)wnd).getRootPane().getClientProperty(JBPopup.KEY); |
| if (popup != null) { |
| if (oldSize.height < newSize.height) { |
| Dimension size = popup.getSize(); |
| size.height += newSize.height - oldSize.height; |
| popup.setSize(size); |
| popup.moveToFitScreen(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public final void doNotSuggestAsParent(final Window window) { |
| myWindowWatcher.doNotSuggestAsParent(window); |
| } |
| |
| @Override |
| public final void dispatchComponentEvent(final ComponentEvent e) { |
| myWindowWatcher.dispatchComponentEvent(e); |
| } |
| |
| @Override |
| @Nullable |
| public final Window suggestParentWindow(@Nullable final Project project) { |
| return myWindowWatcher.suggestParentWindow(project); |
| } |
| |
| @Override |
| @Nullable |
| public final StatusBar getStatusBar(final Project project) { |
| if (!myProject2Frame.containsKey(project)) { |
| return null; |
| } |
| final IdeFrameImpl frame = getFrame(project); |
| LOG.assertTrue(frame != null); |
| return frame.getStatusBar(); |
| } |
| |
| @Override |
| public StatusBar getStatusBar(@NotNull Component c) { |
| return getStatusBar(c, null); |
| } |
| |
| @Override |
| public StatusBar getStatusBar(@NotNull Component c, @Nullable Project project) { |
| Component parent = UIUtil.findUltimateParent(c); |
| if (parent instanceof IdeFrame) { |
| return ((IdeFrame)parent).getStatusBar().findChild(c); |
| } |
| |
| IdeFrame frame = findFrameFor(project); |
| if (frame != null) { |
| return frame.getStatusBar().findChild(c); |
| } |
| |
| assert false : "Cannot find status bar for " + c; |
| |
| return null; |
| } |
| |
| @Override |
| public IdeFrame findFrameFor(@Nullable final Project project) { |
| IdeFrame frame = null; |
| if (project != null) { |
| frame = project.isDefault() ? WelcomeFrame.getInstance() : getFrame(project); |
| if (frame == null) { |
| frame = myProject2Frame.get(null); |
| } |
| } |
| else { |
| Container eachParent = getMostRecentFocusedWindow(); |
| while(eachParent != null) { |
| if (eachParent instanceof IdeFrame) { |
| |
| frame = (IdeFrame)eachParent; |
| break; |
| } |
| eachParent = eachParent.getParent(); |
| } |
| |
| if (frame == null) { |
| frame = tryToFindTheOnlyFrame(); |
| } |
| } |
| |
| return frame; |
| } |
| |
| private static IdeFrame tryToFindTheOnlyFrame() { |
| IdeFrame candidate = null; |
| final Frame[] all = Frame.getFrames(); |
| for (Frame each : all) { |
| if (each instanceof IdeFrame) { |
| if (candidate == null) { |
| candidate = (IdeFrame)each; |
| } else { |
| candidate = null; |
| break; |
| } |
| } |
| } |
| return candidate; |
| } |
| |
| @Override |
| public final IdeFrameImpl getFrame(@Nullable final Project project) { |
| // no assert! otherwise WindowWatcher.suggestParentWindow fails for default project |
| //LOG.assertTrue(myProject2Frame.containsKey(project)); |
| return myProject2Frame.get(project); |
| } |
| |
| @Override |
| public IdeFrame getIdeFrame(@Nullable final Project project) { |
| if (project != null) { |
| return getFrame(project); |
| } |
| final Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); |
| final Component parent = UIUtil.findUltimateParent(window); |
| if (parent instanceof IdeFrame) return (IdeFrame)parent; |
| |
| final Frame[] frames = Frame.getFrames(); |
| for (Frame each : frames) { |
| if (each instanceof IdeFrame) { |
| return (IdeFrame)each; |
| } |
| } |
| |
| return null; |
| } |
| |
| public void showFrame() { |
| final IdeFrameImpl frame = new IdeFrameImpl(myApplicationInfoEx, |
| myActionManager, myUiSettings, myDataManager, |
| ApplicationManager.getApplication()); |
| myProject2Frame.put(null, frame); |
| |
| if (myFrameBounds == null || !ScreenUtil.isVisible(myFrameBounds)) { //avoid situations when IdeFrame is out of all screens |
| final Rectangle rect = ScreenUtil.getMainScreenBounds(); |
| int yParts = rect.height / 6; |
| int xParts = rect.width / 5; |
| myFrameBounds = new Rectangle(xParts, yParts, xParts * 3, yParts * 4); |
| } |
| |
| fixForOracleBug8007219(frame); |
| |
| frame.setBounds(myFrameBounds); |
| frame.setExtendedState(myFrameExtendedState); |
| frame.setVisible(true); |
| |
| } |
| |
| private void fixForOracleBug8007219(IdeFrameImpl frame) { |
| if ((myFrameExtendedState & Frame.MAXIMIZED_BOTH) > 0 && ORACLE_BUG_8007219) { |
| final Rectangle screenBounds = ScreenUtil.getMainScreenBounds(); |
| final Insets screenInsets = ScreenUtil.getScreenInsets(frame.getGraphicsConfiguration()); |
| |
| final int leftGap = myFrameBounds.x - screenInsets.left; |
| |
| myFrameBounds.x = leftGap > ORACLE_BUG_8007219_THRESHOLD ? |
| myFrameBounds.x : |
| screenInsets.left + ORACLE_BUG_8007219_THRESHOLD + 1; |
| |
| final int topGap = myFrameBounds.y - screenInsets.top; |
| |
| myFrameBounds.y = topGap > ORACLE_BUG_8007219_THRESHOLD ? |
| myFrameBounds.y : |
| screenInsets.top + ORACLE_BUG_8007219_THRESHOLD + 1; |
| |
| final int maximumFrameWidth = screenBounds.width - screenInsets.right - myFrameBounds.x; |
| |
| final int rightGap = maximumFrameWidth - myFrameBounds.width; |
| |
| myFrameBounds.width = rightGap > ORACLE_BUG_8007219_THRESHOLD ? |
| myFrameBounds.width : |
| maximumFrameWidth - ORACLE_BUG_8007219_THRESHOLD - 1; |
| |
| final int maximumFrameHeight = screenBounds.height - screenInsets.bottom - myFrameBounds.y; |
| |
| final int bottomGap = maximumFrameHeight - myFrameBounds.height; |
| |
| myFrameBounds.height = bottomGap > ORACLE_BUG_8007219_THRESHOLD ? |
| myFrameBounds.height : |
| - ORACLE_BUG_8007219_THRESHOLD - 1; |
| } |
| } |
| |
| private IdeFrameImpl getDefaultEmptyIdeFrame() { |
| return myProject2Frame.get(null); |
| } |
| |
| @Override |
| public final IdeFrameImpl allocateFrame(final Project project) { |
| LOG.assertTrue(!myProject2Frame.containsKey(project)); |
| |
| final IdeFrameImpl frame; |
| if (myProject2Frame.containsKey(null)) { |
| frame = getDefaultEmptyIdeFrame(); |
| myProject2Frame.remove(null); |
| myProject2Frame.put(project, frame); |
| frame.setProject(project); |
| } |
| else { |
| frame = new IdeFrameImpl(myApplicationInfoEx, myActionManager, myUiSettings, |
| myDataManager, ApplicationManager.getApplication()); |
| |
| final Rectangle bounds = ProjectFrameBounds.getInstance(project).getBounds(); |
| |
| if (bounds != null) { |
| myFrameBounds = bounds; |
| } |
| |
| if (myFrameBounds != null) { |
| fixForOracleBug8007219(frame); |
| frame.setBounds(myFrameBounds); |
| } |
| frame.setProject(project); |
| myProject2Frame.put(project, frame); |
| frame.setExtendedState(myFrameExtendedState); |
| frame.setVisible(true); |
| } |
| |
| frame.addWindowListener(myActivationListener); |
| |
| myEventDispatcher.getMulticaster().frameCreated(frame); |
| |
| return frame; |
| } |
| |
| private void proceedDialogDisposalQueue(Project project) { |
| Set<JDialog> dialogs = myDialogsToDispose.get(project); |
| if (dialogs == null) return; |
| for (JDialog dialog : dialogs) { |
| dialog.dispose(); |
| } |
| myDialogsToDispose.put(project, null); |
| } |
| |
| private void queueForDisposal(JDialog dialog, Project project) { |
| Set<JDialog> dialogs = myDialogsToDispose.get(project); |
| if (dialogs == null) { |
| dialogs = new HashSet<JDialog>(); |
| myDialogsToDispose.put(project, dialogs); |
| } |
| dialogs.add(dialog); |
| } |
| |
| @Override |
| public final void releaseFrame(final IdeFrameImpl frame) { |
| |
| myEventDispatcher.getMulticaster().beforeFrameReleased(frame); |
| |
| final Project project = frame.getProject(); |
| LOG.assertTrue(project != null); |
| |
| frame.removeWindowListener(myActivationListener); |
| proceedDialogDisposalQueue(project); |
| |
| frame.setProject(null); |
| frame.setTitle(null); |
| frame.setFileTitle(null, null); |
| |
| myProject2Frame.remove(project); |
| if (myProject2Frame.isEmpty()) { |
| myProject2Frame.put(null, frame); |
| } |
| else { |
| Disposer.dispose(frame.getStatusBar()); |
| frame.dispose(); |
| } |
| } |
| |
| public final void disposeRootFrame() { |
| if (myProject2Frame.size() == 1) { |
| final IdeFrameImpl rootFrame = myProject2Frame.remove(null); |
| if (rootFrame != null) { |
| // disposing last frame if quitting |
| rootFrame.dispose(); |
| } |
| } |
| } |
| |
| @Override |
| public final Window getMostRecentFocusedWindow() { |
| return myWindowWatcher.getFocusedWindow(); |
| } |
| |
| @Override |
| public final Component getFocusedComponent(@NotNull final Window window) { |
| return myWindowWatcher.getFocusedComponent(window); |
| } |
| |
| @Override |
| @Nullable |
| public final Component getFocusedComponent(@Nullable final Project project) { |
| return myWindowWatcher.getFocusedComponent(project); |
| } |
| |
| /** |
| * Private part |
| */ |
| @Override |
| @NotNull |
| public final CommandProcessor getCommandProcessor() { |
| return myCommandProcessor; |
| } |
| |
| @Override |
| public void loadState(Element state) { |
| final Element frameElement = state.getChild(FRAME_ELEMENT); |
| if (frameElement != null) { |
| myFrameBounds = loadFrameBounds(frameElement); |
| try { |
| myFrameExtendedState = Integer.parseInt(frameElement.getAttributeValue(EXTENDED_STATE_ATTR)); |
| if ((myFrameExtendedState & Frame.ICONIFIED) > 0) { |
| myFrameExtendedState = Frame.NORMAL; |
| } |
| } |
| catch (NumberFormatException ignored) { |
| myFrameExtendedState = Frame.NORMAL; |
| } |
| } |
| |
| final Element desktopElement = state.getChild(DesktopLayout.TAG); |
| if (desktopElement != null) { |
| myLayout.readExternal(desktopElement); |
| } |
| } |
| |
| private static Rectangle loadFrameBounds(final Element frameElement) { |
| Rectangle bounds = new Rectangle(); |
| try { |
| bounds.x = Integer.parseInt(frameElement.getAttributeValue(X_ATTR)); |
| } |
| catch (NumberFormatException ignored) { |
| return null; |
| } |
| try { |
| bounds.y = Integer.parseInt(frameElement.getAttributeValue(Y_ATTR)); |
| } |
| catch (NumberFormatException ignored) { |
| return null; |
| } |
| try { |
| bounds.width = Integer.parseInt(frameElement.getAttributeValue(WIDTH_ATTR)); |
| } |
| catch (NumberFormatException ignored) { |
| return null; |
| } |
| try { |
| bounds.height = Integer.parseInt(frameElement.getAttributeValue(HEIGHT_ATTR)); |
| } |
| catch (NumberFormatException ignored) { |
| return null; |
| } |
| return bounds; |
| } |
| |
| @Nullable |
| @Override |
| public Element getState() { |
| Element frameState = getFrameState(); |
| if (frameState == null) { |
| return null; |
| } |
| |
| Element state = new Element("state"); |
| state.addContent(frameState); |
| |
| // Save default layout |
| Element layoutElement = new Element(DesktopLayout.TAG); |
| state.addContent(layoutElement); |
| myLayout.writeExternal(layoutElement); |
| return state; |
| } |
| |
| private Element getFrameState() { |
| // Save frame bounds |
| final Project[] projects = ProjectManager.getInstance().getOpenProjects(); |
| if (projects.length == 0) { |
| return null; |
| } |
| |
| Project project = projects[0]; |
| final IdeFrameImpl frame = getFrame(project); |
| if (frame == null) { |
| return null; |
| } |
| |
| final Element frameElement = new Element(FRAME_ELEMENT); |
| int extendedState = frame.getExtendedState(); |
| if (SystemInfo.isMacOSLion) { |
| @SuppressWarnings("deprecation") ComponentPeer peer = frame.getPeer(); |
| if (peer instanceof FramePeer) { |
| // frame.state is not updated by jdk so get it directly from peer |
| extendedState = ((FramePeer)peer).getState(); |
| } |
| } |
| boolean isMaximized = extendedState == Frame.MAXIMIZED_BOTH || |
| isFullScreenSupportedInCurrentOS() && frame.isInFullScreen(); |
| boolean usePreviousBounds = isMaximized && |
| myFrameBounds != null && |
| frame.getBounds().contains(new Point((int)myFrameBounds.getCenterX(), (int)myFrameBounds.getCenterY())); |
| 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)); |
| frameElement.setAttribute(HEIGHT_ATTR, Integer.toString(rectangle.height)); |
| |
| if (!(frame.isInFullScreen() && SystemInfo.isAppleJvm)) { |
| frameElement.setAttribute(EXTENDED_STATE_ATTR, Integer.toString(extendedState)); |
| } |
| return frameElement; |
| } |
| |
| @Override |
| public final DesktopLayout getLayout() { |
| return myLayout; |
| } |
| |
| @Override |
| public final void setLayout(final DesktopLayout layout) { |
| myLayout.copyFrom(layout); |
| } |
| |
| @Override |
| @NotNull |
| public final String getComponentName() { |
| return "WindowManager"; |
| } |
| |
| /** |
| * We cannot clear selected menu path just by changing of focused window. Under Windows LAF |
| * focused window changes sporadically when user clicks on menu item or sub-menu. The problem |
| * is that all popups under Windows LAF always has native window ancestor. This window isn't |
| * focusable but by mouse click focused window changes in this manner: |
| * InitialFocusedWindow->null |
| * null->InitialFocusedWindow |
| * To fix this problem we use alarm to accumulate such focus events. |
| */ |
| private static final class SUN_BUG_ID_4218084_Patch implements PropertyChangeListener { |
| private final Alarm myAlarm; |
| private Window myInitialFocusedWindow; |
| private Window myLastFocusedWindow; |
| private final Runnable myClearSelectedPathRunnable; |
| |
| public SUN_BUG_ID_4218084_Patch() { |
| myAlarm = new Alarm(); |
| myClearSelectedPathRunnable = new Runnable() { |
| @Override |
| public void run() { |
| if (myInitialFocusedWindow != myLastFocusedWindow) { |
| MenuSelectionManager.defaultManager().clearSelectedPath(); |
| } |
| } |
| }; |
| } |
| |
| @Override |
| public void propertyChange(final PropertyChangeEvent e) { |
| if (myAlarm.getActiveRequestCount() == 0) { |
| myInitialFocusedWindow = (Window)e.getOldValue(); |
| final MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath(); |
| if (selectedPath.length == 0) { // there is no visible popup |
| return; |
| } |
| Component firstComponent = null; |
| for (final MenuElement menuElement : selectedPath) { |
| final Component component = menuElement.getComponent(); |
| if (component instanceof JMenuBar) { |
| firstComponent = component; |
| break; |
| } else if (component instanceof JPopupMenu) { |
| firstComponent = ((JPopupMenu) component).getInvoker(); |
| break; |
| } |
| } |
| if (firstComponent == null) { |
| return; |
| } |
| final Window window = SwingUtilities.getWindowAncestor(firstComponent); |
| if (window != myInitialFocusedWindow) { // focused window doesn't have popup |
| return; |
| } |
| } |
| myLastFocusedWindow = (Window)e.getNewValue(); |
| myAlarm.cancelAllRequests(); |
| myAlarm.addRequest(myClearSelectedPathRunnable, 150); |
| } |
| } |
| |
| public WindowWatcher getWindowWatcher() { |
| return myWindowWatcher; |
| } |
| |
| @Override |
| public boolean isFullScreenSupportedInCurrentOS() { |
| return SystemInfo.isMacOSLion || SystemInfo.isWindows || SystemInfo.isXWindow && X11UiUtil.isFullScreenSupported(); |
| } |
| |
| public static boolean isFloatingMenuBarSupported() { |
| return !SystemInfo.isMac && getInstance().isFullScreenSupportedInCurrentOS(); |
| } |
| } |