1 /*
   2  * Copyright (c) 1996, 2020, 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 sun.awt.windows;
  27 
  28 import java.awt.Component;
  29 import java.awt.Dimension;
  30 import java.awt.Frame;
  31 import java.awt.GraphicsConfiguration;
  32 import java.awt.MenuBar;
  33 import java.awt.Rectangle;
  34 import java.awt.peer.FramePeer;
  35 import java.security.AccessController;
  36 
  37 import sun.awt.AWTAccessor;
  38 import sun.awt.im.InputMethodManager;
  39 import sun.security.action.GetPropertyAction;
  40 
  41 import static sun.java2d.SunGraphicsEnvironment.getGCDeviceBounds;
  42 import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;
  43 import static sun.java2d.SunGraphicsEnvironment.toUserSpace;
  44 
  45 class WFramePeer extends WWindowPeer implements FramePeer {
  46 
  47     static {
  48         initIDs();
  49     }
  50 
  51     // initialize JNI field and method IDs
  52     private static native void initIDs();
  53 
  54     // FramePeer implementation
  55     @Override
  56     public native void setState(int state);
  57     @Override
  58     public native int getState();
  59 
  60     // sync target and peer
  61     public void setExtendedState(int state) {
  62         AWTAccessor.getFrameAccessor().setExtendedState((Frame)target, state);
  63     }
  64     public int getExtendedState() {
  65         return AWTAccessor.getFrameAccessor().getExtendedState((Frame)target);
  66     }
  67 
  68     // Convenience methods to save us from trouble of extracting
  69     // Rectangle fields in native code.
  70     private native void setMaximizedBounds(int x, int y, int w, int h);
  71     private native void clearMaximizedBounds();
  72 
  73     private static final boolean keepOnMinimize = "true".equals(
  74         AccessController.doPrivileged(
  75             new GetPropertyAction(
  76             "sun.awt.keepWorkingSetOnMinimize")));
  77 
  78     @Override
  79     public final void setMaximizedBounds(Rectangle b) {
  80         if (b == null) {
  81             clearMaximizedBounds();
  82         } else {
  83             b = adjustMaximizedBounds(b);
  84             setMaximizedBounds(b.x, b.y, b.width, b.height);
  85         }
  86     }
  87 
  88     /**
  89      * The incoming bounds describe the maximized size and position of the
  90      * window in the virtual coordinate system. But the window manager expects
  91      * that the bounds are based on the size of the primary monitor and
  92      * position is based on the actual window monitor, even if the window
  93      * ultimately maximizes onto a secondary monitor. And the window manager
  94      * adjusts these values to compensate for differences between the primary
  95      * monitor and the monitor that displays the window.
  96      * <p>
  97      * The method translates the incoming bounds to the values acceptable
  98      * by the window manager. For more details, please refer to 6699851.
  99      */
 100     private Rectangle adjustMaximizedBounds(Rectangle bounds) {
 101         // All calculations should be done in the device space
 102         bounds = toDeviceSpaceAbs(bounds);
 103         GraphicsConfiguration gc = getGraphicsConfiguration();
 104         Rectangle currentDevBounds = getGCDeviceBounds(gc);
 105         // Prepare data for WM_GETMINMAXINFO message.
 106         // ptMaxPosition should be in coordinate system of the current monitor,
 107         // not the main monitor, or monitor on which we maximize the window.
 108         bounds.x -= currentDevBounds.x;
 109         bounds.y -= currentDevBounds.y;
 110         // ptMaxSize will be used as-is if the size is smaller than the main
 111         // monitor. If the size is larger than the main monitor then the
 112         // window manager adjusts the size, like this:
 113         // result = bounds.w + (current.w - main.w); =>> wrong size
 114         // We can try to compensate for this adjustment like this:
 115         // result = bounds.w - (current.w - main.w);
 116         // but this can result to the size smaller than the main screen, so no
 117         // adjustment will be done by the window manager =>> wrong size.
 118         // So we skip compensation here and cut the adjustment on
 119         // WM_WINDOWPOSCHANGING event.
 120         // Note that the result does not depend on the monitor on which we
 121         // maximize the window.
 122         return bounds;
 123     }
 124 
 125     @Override
 126     public boolean updateGraphicsData(GraphicsConfiguration gc) {
 127         boolean result = super.updateGraphicsData(gc);
 128         Rectangle bounds = AWTAccessor.getFrameAccessor().
 129                                getMaximizedBounds((Frame)target);
 130         if (bounds != null) {
 131             setMaximizedBounds(bounds);
 132         }
 133         return result;
 134     }
 135 
 136     @Override
 137     boolean isTargetUndecorated() {
 138         return ((Frame)target).isUndecorated();
 139     }
 140 
 141     @Override
 142     public void reshape(int x, int y, int width, int height) {
 143         if (((Frame)target).isUndecorated()) {
 144             super.reshape(x, y, width, height);
 145         } else {
 146             reshapeFrame(x, y, width, height);
 147         }
 148     }
 149 
 150     @Override
 151     public final Dimension getMinimumSize() {
 152         GraphicsConfiguration gc = getGraphicsConfiguration();
 153         Dimension d = new Dimension();
 154         if (!((Frame)target).isUndecorated()) {
 155             d.setSize(toUserSpace(gc, getSysMinWidth(), getSysMinHeight()));
 156         }
 157         if (((Frame) target).getMenuBar() != null) {
 158             d.height += toUserSpace(gc, 0, getSysMenuHeight()).height;
 159         }
 160         return d;
 161     }
 162 
 163     // Note: Because this method calls resize(), which may be overridden
 164     // by client code, this method must not be executed on the toolkit
 165     // thread.
 166     @Override
 167     public void setMenuBar(MenuBar mb) {
 168         WMenuBarPeer mbPeer = (WMenuBarPeer) WToolkit.targetToPeer(mb);
 169         if (mbPeer != null) {
 170             if (mbPeer.framePeer != this) {
 171                 mb.removeNotify();
 172                 mb.addNotify();
 173                 mbPeer = (WMenuBarPeer) WToolkit.targetToPeer(mb);
 174                 if (mbPeer != null && mbPeer.framePeer != this) {
 175                     throw new IllegalStateException("Wrong parent peer");
 176                 }
 177             }
 178             if (mbPeer != null) {
 179                 addChildPeer(mbPeer);
 180             }
 181         }
 182         setMenuBar0(mbPeer);
 183         updateInsets(insets_);
 184     }
 185 
 186     // Note: Because this method calls resize(), which may be overridden
 187     // by client code, this method must not be executed on the toolkit
 188     // thread.
 189     private native void setMenuBar0(WMenuBarPeer mbPeer);
 190 
 191     // Toolkit & peer internals
 192 
 193     WFramePeer(Frame target) {
 194         super(target);
 195 
 196         InputMethodManager imm = InputMethodManager.getInstance();
 197         String menuString = imm.getTriggerMenuString();
 198         if (menuString != null)
 199         {
 200           pSetIMMOption(menuString);
 201         }
 202     }
 203 
 204     native void createAwtFrame(WComponentPeer parent);
 205     @Override
 206     void create(WComponentPeer parent) {
 207         preCreate(parent);
 208         createAwtFrame(parent);
 209     }
 210 
 211     @Override
 212     void initialize() {
 213         super.initialize();
 214 
 215         Frame target = (Frame)this.target;
 216 
 217         if (target.getTitle() != null) {
 218             setTitle(target.getTitle());
 219         }
 220         setResizable(target.isResizable());
 221         setState(target.getExtendedState());
 222     }
 223 
 224     private static native int getSysMenuHeight();
 225 
 226     native void pSetIMMOption(String option);
 227     void notifyIMMOptionChange(){
 228       InputMethodManager.getInstance().notifyChangeRequest((Component)target);
 229     }
 230 
 231     @Override
 232     public void setBoundsPrivate(int x, int y, int width, int height) {
 233         setBounds(x, y, width, height, SET_BOUNDS);
 234     }
 235     @Override
 236     public Rectangle getBoundsPrivate() {
 237         return getBounds();
 238     }
 239 
 240     // TODO: implement it in peers. WLightweightFramePeer may implement lw version.
 241     @Override
 242     public void emulateActivation(boolean activate) {
 243         synthesizeWmActivate(activate);
 244     }
 245 
 246     private native void synthesizeWmActivate(boolean activate);
 247 }