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.event.AdjustmentEvent;
  29 import java.awt.peer.ScrollPanePeer;
  30 
  31 import sun.awt.AWTAccessor;
  32 import sun.awt.AWTAccessor.ComponentAccessor;
  33 import sun.awt.PeerEvent;
  34 
  35 import sun.util.logging.PlatformLogger;
  36 
  37 final class WScrollPanePeer extends WPanelPeer implements ScrollPanePeer {
  38 
  39     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.windows.WScrollPanePeer");
  40 
  41     int scrollbarWidth;
  42     int scrollbarHeight;
  43     int prevx;
  44     int prevy;
  45 
  46     static {
  47         initIDs();
  48     }
  49 
  50     static native void initIDs();
  51     @Override
  52     native void create(WComponentPeer parent);
  53     native int getOffset(int orient);
  54 
  55     WScrollPanePeer(Component target) {
  56         super(target);
  57         scrollbarWidth = _getVScrollbarWidth();
  58         scrollbarHeight = _getHScrollbarHeight();
  59     }
  60 
  61     @Override
  62     void initialize() {
  63         super.initialize();
  64         setInsets();
  65         Insets i = getInsets();
  66         setScrollPosition(-i.left,-i.top);
  67     }
  68 
  69     @Override
  70     public void setUnitIncrement(Adjustable adj, int p) {
  71         // The unitIncrement is grabbed from the target as needed.
  72     }
  73 
  74     private native void setInsets();
  75 
  76     @Override
  77     public synchronized native void setScrollPosition(int x, int y);
  78 
  79     @Override
  80     public int getHScrollbarHeight() {
  81         return scrollbarHeight;
  82     }
  83     private native int _getHScrollbarHeight();
  84 
  85     @Override
  86     public int getVScrollbarWidth() {
  87         return scrollbarWidth;
  88     }
  89     private native int _getVScrollbarWidth();
  90 
  91     public Point getScrollOffset() {
  92         int x = getOffset(Adjustable.HORIZONTAL);
  93         int y = getOffset(Adjustable.VERTICAL);
  94         return new Point(x, y);
  95     }
  96 
  97     /**
  98      * The child component has been resized.  The scrollbars must be
  99      * updated with the new sizes.  At the native level the sizes of
 100      * the actual windows may not have changed yet, so the size
 101      * information from the java-level is passed down and used.
 102      */
 103     @Override
 104     public void childResized(int width, int height) {
 105         ScrollPane sp = (ScrollPane)target;
 106         Dimension vs = sp.getSize();
 107         setSpans(vs.width, vs.height, width, height);
 108         setInsets();
 109     }
 110 
 111     synchronized native void setSpans(int viewWidth, int viewHeight,
 112                                       int childWidth, int childHeight);
 113 
 114     /**
 115      * Called by ScrollPane's internal observer of the scrollpane's adjustables.
 116      * This is called whenever a scroll position is changed in one
 117      * of adjustables, whether it was modified externally or from the
 118      * native scrollbars themselves.
 119      */
 120     @Override
 121     public void setValue(Adjustable adj, int v) {
 122         Component c = getScrollChild();
 123         if (c == null) {
 124             return;
 125         }
 126 
 127         Point p = c.getLocation();
 128         switch(adj.getOrientation()) {
 129         case Adjustable.VERTICAL:
 130             setScrollPosition(-(p.x), v);
 131             break;
 132         case Adjustable.HORIZONTAL:
 133             setScrollPosition(v, -(p.y));
 134             break;
 135         }
 136     }
 137 
 138     private Component getScrollChild() {
 139         ScrollPane sp = (ScrollPane)target;
 140         Component child = null;
 141         try {
 142             child = sp.getComponent(0);
 143         } catch (ArrayIndexOutOfBoundsException e) {
 144             // do nothing.  in this case we return null
 145         }
 146         return child;
 147     }
 148 
 149     /*
 150      * Called from Windows in response to WM_VSCROLL/WM_HSCROLL message
 151      */
 152     private void postScrollEvent(int orient, int type,
 153                                  int pos, boolean isAdjusting)
 154     {
 155         Runnable adjustor = new Adjustor(orient, type, pos, isAdjusting);
 156         WToolkit.executeOnEventHandlerThread(new ScrollEvent(target, adjustor));
 157     }
 158 
 159     /*
 160      * Event that executes on the Java dispatch thread to move the
 161      * scroll bar thumbs and paint the exposed area in one synchronous
 162      * operation.
 163      */
 164     @SuppressWarnings("serial") // JDK-implementation class
 165     class ScrollEvent extends PeerEvent {
 166         ScrollEvent(Object source, Runnable runnable) {
 167             super(source, runnable, 0L);
 168         }
 169 
 170         @Override
 171         public PeerEvent coalesceEvents(PeerEvent newEvent) {
 172             if (log.isLoggable(PlatformLogger.Level.FINEST)) {
 173                 log.finest("ScrollEvent coalesced: " + newEvent);
 174             }
 175             if (newEvent instanceof ScrollEvent) {
 176                 return newEvent;
 177             }
 178             return null;
 179         }
 180     }
 181 
 182     /*
 183      * Runnable for the ScrollEvent that performs the adjustment.
 184      */
 185     class Adjustor implements Runnable {
 186         int orient;             // selects scrollbar
 187         int type;               // adjustment type
 188         int pos;                // new position (only used for absolute)
 189         boolean isAdjusting;    // isAdjusting status
 190 
 191         Adjustor(int orient, int type, int pos, boolean isAdjusting) {
 192             this.orient = orient;
 193             this.type = type;
 194             this.pos = pos;
 195             this.isAdjusting = isAdjusting;
 196         }
 197 
 198         @Override
 199         public void run() {
 200             if (getScrollChild() == null) {
 201                 return;
 202             }
 203             ScrollPane sp = (ScrollPane)WScrollPanePeer.this.target;
 204             ScrollPaneAdjustable adj = null;
 205 
 206             // ScrollPaneAdjustable made public in 1.4, but
 207             // get[HV]Adjustable can't be declared to return
 208             // ScrollPaneAdjustable because it would break backward
 209             // compatibility -- hence the cast
 210 
 211             if (orient == Adjustable.VERTICAL) {
 212                 adj = (ScrollPaneAdjustable)sp.getVAdjustable();
 213             } else if (orient == Adjustable.HORIZONTAL) {
 214                 adj = (ScrollPaneAdjustable)sp.getHAdjustable();
 215             } else {
 216                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
 217                     log.fine("Assertion failed: unknown orient");
 218                 }
 219             }
 220 
 221             if (adj == null) {
 222                 return;
 223             }
 224 
 225             int newpos = adj.getValue();
 226             switch (type) {
 227               case AdjustmentEvent.UNIT_DECREMENT:
 228                   newpos -= adj.getUnitIncrement();
 229                   break;
 230               case AdjustmentEvent.UNIT_INCREMENT:
 231                   newpos += adj.getUnitIncrement();
 232                   break;
 233               case AdjustmentEvent.BLOCK_DECREMENT:
 234                   newpos -= adj.getBlockIncrement();
 235                   break;
 236               case AdjustmentEvent.BLOCK_INCREMENT:
 237                   newpos += adj.getBlockIncrement();
 238                   break;
 239               case AdjustmentEvent.TRACK:
 240                   newpos = this.pos;
 241                   break;
 242               default:
 243                   if (log.isLoggable(PlatformLogger.Level.FINE)) {
 244                       log.fine("Assertion failed: unknown type");
 245                   }
 246                   return;
 247             }
 248 
 249             // keep scroll position in acceptable range
 250             newpos = Math.max(adj.getMinimum(), newpos);
 251             newpos = Math.min(adj.getMaximum(), newpos);
 252 
 253             // set value, this will synchronously fire an AdjustmentEvent
 254             adj.setValueIsAdjusting(isAdjusting);
 255 
 256             // Fix for 4075484 - consider type information when creating AdjustmentEvent
 257             // We can't just call adj.setValue() because it creates AdjustmentEvent with type=TRACK
 258             // Instead, we call private method setTypedValue of ScrollPaneAdjustable.
 259             AWTAccessor.getScrollPaneAdjustableAccessor().setTypedValue(adj,
 260                                                                         newpos,
 261                                                                         type);
 262 
 263             // Paint the exposed area right away.  To do this - find
 264             // the heavyweight ancestor of the scroll child.
 265             Component hwAncestor = getScrollChild();
 266             final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
 267             while (hwAncestor != null
 268                    && !(acc.getPeer(hwAncestor) instanceof WComponentPeer))
 269             {
 270                 hwAncestor = hwAncestor.getParent();
 271             }
 272             if (log.isLoggable(PlatformLogger.Level.FINE)) {
 273                 if (hwAncestor == null) {
 274                     log.fine("Assertion (hwAncestor != null) failed, " +
 275                              "couldn't find heavyweight ancestor of scroll pane child");
 276                 }
 277             }
 278             WComponentPeer hwPeer = acc.getPeer(hwAncestor);
 279             hwPeer.paintDamagedAreaImmediately();
 280         }
 281     }
 282 
 283 }