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