1 /*
   2  * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.apple.eawt;
  27 
  28 import java.awt.Container;
  29 import java.awt.Frame;
  30 
  31 
  32 import javax.swing.JFrame;
  33 import javax.swing.JLayeredPane;
  34 import javax.swing.JMenuBar;
  35 import javax.swing.plaf.MenuBarUI;
  36 
  37 import com.apple.laf.ScreenMenuBar;
  38 import sun.awt.AWTAccessor;
  39 import sun.lwawt.macosx.CMenuBar;
  40 
  41 import com.apple.laf.AquaMenuBarUI;
  42 
  43 class _AppMenuBarHandler {
  44     private static final int MENU_ABOUT = 1;
  45     private static final int MENU_PREFS = 2;
  46 
  47     private static native void nativeSetMenuState(final int menu, final boolean visible, final boolean enabled);
  48     private static native void nativeSetDefaultMenuBar(final long menuBarPeer);
  49 
  50     static final _AppMenuBarHandler instance = new _AppMenuBarHandler();
  51     static _AppMenuBarHandler getInstance() {
  52         return instance;
  53     }
  54 
  55     // callback from the native delegate -init function
  56     private static void initMenuStates(final boolean aboutMenuItemVisible,
  57                                        final boolean aboutMenuItemEnabled,
  58                                        final boolean prefsMenuItemVisible,
  59                                        final boolean prefsMenuItemEnabled) {
  60         synchronized (instance) {
  61             instance.aboutMenuItemVisible = aboutMenuItemVisible;
  62             instance.aboutMenuItemEnabled = aboutMenuItemEnabled;
  63             instance.prefsMenuItemVisible = prefsMenuItemVisible;
  64             instance.prefsMenuItemEnabled = prefsMenuItemEnabled;
  65         }
  66     }
  67 
  68     _AppMenuBarHandler() { }
  69 
  70     boolean aboutMenuItemVisible;
  71     boolean aboutMenuItemEnabled;
  72 
  73     boolean prefsMenuItemVisible;
  74     boolean prefsMenuItemEnabled;
  75     boolean prefsMenuItemExplicitlySet;
  76 
  77     void setDefaultMenuBar(final JMenuBar menuBar) {
  78         installDefaultMenuBar(menuBar);
  79 
  80         // scan the current frames, and see if any are foreground
  81         final Frame[] frames = Frame.getFrames();
  82         for (final Frame frame : frames) {
  83             if (frame.isVisible() && !isFrameMinimized(frame)) {
  84                 return;
  85             }
  86         }
  87 
  88         // if we have no foreground frames, then we have to "kick" the menubar
  89         final JFrame pingFrame = new JFrame();
  90         pingFrame.getRootPane().putClientProperty("Window.alpha", Float.valueOf(0.0f));
  91         pingFrame.setUndecorated(true);
  92         pingFrame.setVisible(true);
  93         pingFrame.toFront();
  94         pingFrame.setVisible(false);
  95         pingFrame.dispose();
  96     }
  97 
  98     static boolean isFrameMinimized(final Frame frame) {
  99         return (frame.getExtendedState() & Frame.ICONIFIED) != 0;
 100     }
 101 
 102     static void installDefaultMenuBar(final JMenuBar menuBar) {
 103         if (menuBar == null) {
 104             // intentionally clearing the default menu
 105             nativeSetDefaultMenuBar(0);
 106             return;
 107         }
 108 
 109         Container parent = menuBar.getParent();
 110         if (parent instanceof JLayeredPane) {
 111             ((JLayeredPane) parent).remove(menuBar);
 112         }
 113 
 114         MenuBarUI ui = menuBar.getUI();
 115         if (!(ui instanceof AquaMenuBarUI)) {
 116             ui = new AquaMenuBarUI();
 117             menuBar.setUI(ui);
 118         }
 119 
 120         final AquaMenuBarUI aquaUI = (AquaMenuBarUI)ui;
 121         final ScreenMenuBar screenMenuBar = aquaUI.getScreenMenuBar();
 122         if (screenMenuBar == null) {
 123             // Aqua is installed, but we aren't using the screen menu bar
 124             throw new IllegalStateException("Application.setDefaultMenuBar() only works if apple.laf.useScreenMenuBar=true");
 125         }
 126 
 127         screenMenuBar.addNotify();
 128         final Object peer = AWTAccessor.getMenuComponentAccessor().getPeer(screenMenuBar);
 129         if (!(peer instanceof CMenuBar)) {
 130             // such a thing should not be possible
 131             throw new IllegalStateException("Unable to determine native menu bar from provided JMenuBar");
 132         }
 133 
 134         // grab the pointer to the CMenuBar, and retain it in native
 135         ((CMenuBar) peer).execute(_AppMenuBarHandler::nativeSetDefaultMenuBar);
 136     }
 137 
 138     void setAboutMenuItemVisible(final boolean present) {
 139         synchronized (this) {
 140             if (aboutMenuItemVisible == present) return;
 141             aboutMenuItemVisible = present;
 142         }
 143 
 144         nativeSetMenuState(MENU_ABOUT, aboutMenuItemVisible, aboutMenuItemEnabled);
 145     }
 146 
 147     void setPreferencesMenuItemVisible(final boolean present) {
 148         synchronized (this) {
 149             prefsMenuItemExplicitlySet = true;
 150             if (prefsMenuItemVisible == present) return;
 151             prefsMenuItemVisible = present;
 152         }
 153         nativeSetMenuState(MENU_PREFS, prefsMenuItemVisible, prefsMenuItemEnabled);
 154     }
 155 
 156     void setAboutMenuItemEnabled(final boolean enable) {
 157         synchronized (this) {
 158             if (aboutMenuItemEnabled == enable) return;
 159             aboutMenuItemEnabled = enable;
 160         }
 161         nativeSetMenuState(MENU_ABOUT, aboutMenuItemVisible, aboutMenuItemEnabled);
 162     }
 163 
 164     void setPreferencesMenuItemEnabled(final boolean enable) {
 165         synchronized (this) {
 166             prefsMenuItemExplicitlySet = true;
 167             if (prefsMenuItemEnabled == enable) return;
 168             prefsMenuItemEnabled = enable;
 169         }
 170         nativeSetMenuState(MENU_PREFS, prefsMenuItemVisible, prefsMenuItemEnabled);
 171     }
 172 
 173     boolean isAboutMenuItemVisible() {
 174         return aboutMenuItemVisible;
 175     }
 176 
 177     boolean isPreferencesMenuItemVisible() {
 178         return prefsMenuItemVisible;
 179     }
 180 
 181     boolean isAboutMenuItemEnabled() {
 182         return aboutMenuItemEnabled;
 183     }
 184 
 185     boolean isPreferencesMenuItemEnabled() {
 186         return prefsMenuItemEnabled;
 187     }
 188 }