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