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