1 /*
   2  * Copyright (c) 1996, 2018, 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 package sun.awt.windows;
  26 
  27 import java.awt.*;
  28 import java.awt.peer.*;
  29 import sun.awt.AWTAccessor;
  30 import sun.awt.im.InputMethodManager;
  31 import java.security.AccessController;
  32 import sun.security.action.GetPropertyAction;
  33 import sun.java2d.pipe.Region;
  34 
  35 class WFramePeer extends WWindowPeer implements FramePeer {
  36 
  37     static {
  38         initIDs();
  39     }
  40 
  41     // initialize JNI field and method IDs
  42     private static native void initIDs();
  43 
  44     // FramePeer implementation
  45     @Override
  46     public native void setState(int state);
  47     @Override
  48     public native int getState();
  49 
  50     // sync target and peer
  51     public void setExtendedState(int state) {
  52         AWTAccessor.getFrameAccessor().setExtendedState((Frame)target, state);
  53     }
  54     public int getExtendedState() {
  55         return AWTAccessor.getFrameAccessor().getExtendedState((Frame)target);
  56     }
  57 
  58     // Convenience methods to save us from trouble of extracting
  59     // Rectangle fields in native code.
  60     private native void setMaximizedBounds(int x, int y, int w, int h);
  61     private native void clearMaximizedBounds();
  62 
  63     private static final boolean keepOnMinimize = "true".equals(
  64         AccessController.doPrivileged(
  65             new GetPropertyAction(
  66             "sun.awt.keepWorkingSetOnMinimize")));
  67 
  68     @Override
  69     public void setMaximizedBounds(Rectangle b) {
  70         if (b == null) {
  71             clearMaximizedBounds();
  72         } else {
  73             Rectangle adjBounds = adjustMaximizedBounds(b);
  74             setMaximizedBounds(adjBounds.x, adjBounds.y, adjBounds.width, adjBounds.height);
  75         }
  76     }
  77 
  78     /**
  79      * The incoming bounds describe the maximized size and position of the
  80      * window on the monitor that displays the window. But the window manager
  81      * expects that the bounds are based on the size and position of the
  82      * primary monitor, even if the window ultimately maximizes onto a
  83      * secondary monitor. And the window manager adjusts these values to
  84      * compensate for differences between the primary monitor and the monitor
  85      * that displays the window.
  86      * The method translates the incoming bounds to the values acceptable
  87      * by the window manager. For more details, please refer to 6699851.
  88      */
  89     private Rectangle adjustMaximizedBounds(Rectangle b) {
  90         GraphicsConfiguration currentDevGC = getGraphicsConfiguration();
  91 
  92         GraphicsDevice primaryDev = GraphicsEnvironment
  93             .getLocalGraphicsEnvironment().getDefaultScreenDevice();
  94         GraphicsConfiguration primaryDevGC = primaryDev.getDefaultConfiguration();
  95 
  96         if (currentDevGC != null && currentDevGC != primaryDevGC) {
  97             double currScaleX = currentDevGC.getDefaultTransform().getScaleX();
  98             double currScaleY = currentDevGC.getDefaultTransform().getScaleY();
  99             double priScaleX = primaryDevGC.getDefaultTransform().getScaleX();
 100             double priScaleY = primaryDevGC.getDefaultTransform().getScaleY();
 101             Rectangle currentDevBounds = currentDevGC.getBounds();
 102             Rectangle primaryDevBounds = primaryDevGC.getBounds();
 103 
 104             //scale the bounds for proper comparisions.
 105             currentDevBounds = scaleBounds(currentDevBounds, currScaleX, currScaleY);
 106             b = scaleBounds(b, currScaleX, currScaleY);
 107             Rectangle scaledPrimaryDevBounds = scaleBounds(primaryDevBounds, priScaleX, priScaleY);
 108 
 109             //due to floating point operations, it could be possible in some
 110             //scenarios, that the width and height are slightly larger
 111             //than the maximum permissible values.
 112             if (b.width > currentDevBounds.width) {
 113                 b.width = currentDevBounds.width;
 114             }
 115 
 116             if (b.height > currentDevBounds.height) {
 117                 b.height = currentDevBounds.height;
 118             }
 119 
 120             //To maximize a window onto secondary screen, we still need to
 121             //provide the primary screen coordinates, and set the width
 122             //and height equal to primary screen bounds.
 123             if (scaledPrimaryDevBounds.width < currentDevBounds.width &&
 124                 b.width == currentDevBounds.width) {
 125                 b.width = primaryDevBounds.width;
 126             }
 127 
 128             if (scaledPrimaryDevBounds.height < currentDevBounds.height &&
 129                 b.height == currentDevBounds.height) {
 130                 b.height = primaryDevBounds.height;
 131             }
 132 
 133             if (b.x == currentDevBounds.x) {
 134                 b.x = primaryDevBounds.x;
 135             }
 136 
 137             if (b.y == currentDevBounds.y) {
 138                 b.y = primaryDevBounds.y;
 139             }
 140 
 141             return b;
 142         } else {
 143             double priScaleX = primaryDevGC.getDefaultTransform().getScaleX();
 144             double priScaleY = primaryDevGC.getDefaultTransform().getScaleY();
 145             return scaleBounds(b, priScaleX, priScaleY);
 146         }
 147     }
 148 
 149     private Rectangle scaleBounds(Rectangle source, double scaleX, double scaleY) {
 150         Rectangle res = (Rectangle)source.clone();
 151 
 152         if (scaleX > 1.0) {
 153             res.x = Region.clipScale(source.x, scaleX);
 154             res.width = Region.clipScale(source.width,  scaleX);
 155         }
 156 
 157         if (scaleY > 1.0) {
 158             res.y = Region.clipScale(source.y, scaleY);
 159             res.height = Region.clipScale(source.height, scaleY);
 160         }
 161 
 162         return res;
 163     }
 164 
 165     @Override
 166     public boolean updateGraphicsData(GraphicsConfiguration gc) {
 167         boolean result = super.updateGraphicsData(gc);
 168         Rectangle bounds = AWTAccessor.getFrameAccessor().
 169                                getMaximizedBounds((Frame)target);
 170         if (bounds != null) {
 171             setMaximizedBounds(bounds);
 172         }
 173         return result;
 174     }
 175 
 176     @Override
 177     boolean isTargetUndecorated() {
 178         return ((Frame)target).isUndecorated();
 179     }
 180 
 181     @Override
 182     public void reshape(int x, int y, int width, int height) {
 183         if (((Frame)target).isUndecorated()) {
 184             super.reshape(x, y, width, height);
 185         } else {
 186             reshapeFrame(x, y, width, height);
 187         }
 188     }
 189 
 190     @Override
 191     public Dimension getMinimumSize() {
 192         Dimension d = new Dimension();
 193         if (!((Frame)target).isUndecorated()) {
 194             d.setSize(getSysMinWidth(), getSysMinHeight());
 195         }
 196         if (((Frame)target).getMenuBar() != null) {
 197             d.height += getSysMenuHeight();
 198         }
 199         return d;
 200     }
 201 
 202     // Note: Because this method calls resize(), which may be overridden
 203     // by client code, this method must not be executed on the toolkit
 204     // thread.
 205     @Override
 206     public void setMenuBar(MenuBar mb) {
 207         WMenuBarPeer mbPeer = (WMenuBarPeer) WToolkit.targetToPeer(mb);
 208         if (mbPeer != null) {
 209             if (mbPeer.framePeer != this) {
 210                 mb.removeNotify();
 211                 mb.addNotify();
 212                 mbPeer = (WMenuBarPeer) WToolkit.targetToPeer(mb);
 213                 if (mbPeer != null && mbPeer.framePeer != this) {
 214                     throw new IllegalStateException("Wrong parent peer");
 215                 }
 216             }
 217             if (mbPeer != null) {
 218                 addChildPeer(mbPeer);
 219             }
 220         }
 221         setMenuBar0(mbPeer);
 222         updateInsets(insets_);
 223     }
 224 
 225     // Note: Because this method calls resize(), which may be overridden
 226     // by client code, this method must not be executed on the toolkit
 227     // thread.
 228     private native void setMenuBar0(WMenuBarPeer mbPeer);
 229 
 230     // Toolkit & peer internals
 231 
 232     WFramePeer(Frame target) {
 233         super(target);
 234 
 235         InputMethodManager imm = InputMethodManager.getInstance();
 236         String menuString = imm.getTriggerMenuString();
 237         if (menuString != null)
 238         {
 239           pSetIMMOption(menuString);
 240         }
 241     }
 242 
 243     native void createAwtFrame(WComponentPeer parent);
 244     @Override
 245     void create(WComponentPeer parent) {
 246         preCreate(parent);
 247         createAwtFrame(parent);
 248     }
 249 
 250     @Override
 251     void initialize() {
 252         super.initialize();
 253 
 254         Frame target = (Frame)this.target;
 255 
 256         if (target.getTitle() != null) {
 257             setTitle(target.getTitle());
 258         }
 259         setResizable(target.isResizable());
 260         setState(target.getExtendedState());
 261     }
 262 
 263     private static native int getSysMenuHeight();
 264 
 265     native void pSetIMMOption(String option);
 266     void notifyIMMOptionChange(){
 267       InputMethodManager.getInstance().notifyChangeRequest((Component)target);
 268     }
 269 
 270     @Override
 271     public void setBoundsPrivate(int x, int y, int width, int height) {
 272         setBounds(x, y, width, height, SET_BOUNDS);
 273     }
 274     @Override
 275     public Rectangle getBoundsPrivate() {
 276         return getBounds();
 277     }
 278 
 279     // TODO: implement it in peers. WLightweightFramePeer may implement lw version.
 280     @Override
 281     public void emulateActivation(boolean activate) {
 282         synthesizeWmActivate(activate);
 283     }
 284 
 285     private native void synthesizeWmActivate(boolean activate);
 286 }