1/*
2 * Copyright (c) 2002, 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
26package sun.awt.X11;
27
28import java.awt.*;
29import java.awt.event.*;
30import java.awt.peer.*;
31import java.lang.reflect.*;
32
33import sun.awt.AWTAccessor;
34
35class XScrollPanePeer extends XComponentPeer implements ScrollPanePeer, XScrollbarClient {
36
37    public static final int     MARGIN = 1;
38    public static final int     SCROLLBAR;
39    public static final int     SPACE = 2;
40    public static final int     SCROLLBAR_INSET = 2;
41
42    public static final int     VERTICAL = 1 << 0;
43    public static final int     HORIZONTAL = 1 << 1;
44
45    static {
46        SCROLLBAR = XToolkit.getUIDefaults().getInt("ScrollBar.defaultWidth");
47    }
48
49    XVerticalScrollbar       vsb;
50    XHorizontalScrollbar     hsb;
51    XWindow                  clip;
52
53    int                         active=VERTICAL;
54    int                         hsbSpace;
55    int                         vsbSpace;
56
57    static class XScrollPaneContentWindow extends XWindow {
58        XScrollPaneContentWindow(ScrollPane target, long parentWindow) {
59            super(target, parentWindow);
60        }
61        public String getWMName() {
62            return "ScrollPane content";
63        }
64    }
65
66    XScrollPanePeer(ScrollPane target) {
67        super(target);
68
69        // Create the clip window. The field "clip" must be null when
70        // we call winCreate, or the parent of clip will be set to itself!
71        clip = null;
72
73
74        XWindow c = new XScrollPaneContentWindow(target,window);
75        clip = c;
76
77        vsb = new XVerticalScrollbar(this);
78
79        hsb = new XHorizontalScrollbar(this);
80
81        if (target.getScrollbarDisplayPolicy() == ScrollPane.SCROLLBARS_ALWAYS) {
82            vsbSpace = hsbSpace = SCROLLBAR;
83        } else {
84            vsbSpace = hsbSpace = 0;
85        }
86
87        int unitIncrement = 1;
88        Adjustable vAdjustable = target.getVAdjustable();
89        if (vAdjustable != null){
90            unitIncrement = vAdjustable.getUnitIncrement();
91        }
92        int h = height-hsbSpace;
93        vsb.setValues(0, h, 0, h, unitIncrement, Math.max(1, (int)(h * 0.90)));
94        vsb.setSize(vsbSpace-SCROLLBAR_INSET, h);
95
96        unitIncrement = 1;
97        Adjustable hAdjustable = target.getHAdjustable();
98        if (hAdjustable != null){
99            unitIncrement = hAdjustable.getUnitIncrement();
100        }
101        int w = width - vsbSpace;
102        hsb.setValues(0, w, 0, w, unitIncrement, Math.max(1, (int)(w * 0.90)));
103        hsb.setSize(w, hsbSpace-SCROLLBAR_INSET);
104
105        setViewportSize();
106        clip.xSetVisible(true);
107
108
109    }
110
111    public long getContentWindow()
112    {
113        return (clip == null) ? window : clip.getWindow();
114    }
115
116    public void setBounds(int x, int y, int w, int h, int op) {
117        super.setBounds(x, y, w, h, op);
118
119        if (clip == null) return;
120        setScrollbarSpace();
121        setViewportSize();
122        repaint();
123    }
124
125    public Insets getInsets() {
126        return new Insets(MARGIN, MARGIN, MARGIN+hsbSpace, MARGIN+vsbSpace);
127    }
128
129    public int getHScrollbarHeight() {
130        return SCROLLBAR;
131    }
132
133    public int getVScrollbarWidth() {
134        return SCROLLBAR;
135    }
136
137    public void childResized(int w, int h) {
138        if (setScrollbarSpace()) {
139            setViewportSize();
140        }
141        repaint();
142    }
143
144    @SuppressWarnings("deprecation")
145    Dimension getChildSize() {
146        ScrollPane sp = (ScrollPane)target;
147        if (sp.countComponents() > 0) {
148            Component c = sp.getComponent(0);
149            return c.size();
150        } else {
151            return new Dimension(0, 0);
152        }
153    }
154
155    @SuppressWarnings("deprecation")
156    boolean setScrollbarSpace() {
157        ScrollPane sp = (ScrollPane)target;
158        boolean changed = false;
159        int sbDisplayPolicy = sp.getScrollbarDisplayPolicy();
160
161        if (sbDisplayPolicy == ScrollPane.SCROLLBARS_NEVER) {
162            return changed;
163        }
164        Dimension cSize = getChildSize();
165
166        if (sbDisplayPolicy == ScrollPane.SCROLLBARS_AS_NEEDED) {
167            int oldHsbSpace = hsbSpace;
168            int oldVsbSpace = vsbSpace;
169            hsbSpace = (cSize.width <= (width - 2*MARGIN) ? 0 : SCROLLBAR);
170            vsbSpace = (cSize.height <= (height - 2*MARGIN) ? 0 : SCROLLBAR);
171
172            if (hsbSpace == 0 && vsbSpace != 0) {
173                hsbSpace = (cSize.width <= (width - SCROLLBAR - 2*MARGIN) ? 0 : SCROLLBAR);
174            }
175            if (vsbSpace == 0 && hsbSpace != 0) {
176                vsbSpace = (cSize.height <= (height - SCROLLBAR - 2*MARGIN) ? 0 : SCROLLBAR);
177            }
178            if (oldHsbSpace != hsbSpace || oldVsbSpace != vsbSpace) {
179                changed = true;
180            }
181        }
182        if (vsbSpace > 0) {
183            int vis = height - (2*MARGIN) - hsbSpace;
184            int max = Math.max(cSize.height, vis);
185            vsb.setValues(vsb.getValue(), vis, 0, max);
186            vsb.setBlockIncrement((int)(vsb.getVisibleAmount() * .90));
187            vsb.setSize(vsbSpace-SCROLLBAR_INSET, height-hsbSpace);
188            // Adjustable vadj = sp.getVAdjustable();
189            // vadj.setVisibleAmount(vsb.vis);
190            // vadj.setMaximum(vsb.max);
191            // vadj.setBlockIncrement(vsb.page);
192        }
193        if (hsbSpace > 0) {
194            int vis = width - (2*MARGIN) - vsbSpace;
195            int max = Math.max(cSize.width, vis);
196            hsb.setValues(hsb.getValue(), vis, 0, max);
197            hsb.setBlockIncrement((int)(hsb.getVisibleAmount() * .90));
198            hsb.setSize(width-vsbSpace, hsbSpace-SCROLLBAR_INSET);
199            // Adjustable hadj = sp.getHAdjustable();
200            // hadj.setVisibleAmount(hsb.vis);
201            // hadj.setMaximum(hsb.max);
202            // hadj.setBlockIncrement(hsb.page);
203        }
204
205        // Check to see if we hid either of the scrollbars but left
206        // ourselves scrolled off of the top and/or right of the pane.
207        // If we did, we need to scroll to the top and/or right of
208        // the pane to make it visible.
209        //
210        // Reminder: see if there is a better place to put this code.
211        boolean must_scroll = false;
212
213        // Get the point at which the ScrollPane is currently located
214        // if number of components > 0
215        Point p = new Point(0, 0);
216
217        if (((ScrollPane)target).getComponentCount() > 0){
218
219            p = ((ScrollPane)target).getComponent(0).location();
220
221            if ((vsbSpace == 0) && (p.y < 0)) {
222                p.y = 0;
223                must_scroll = true;
224            }
225
226            if ((hsbSpace == 0) && (p.x < 0)) {
227                p.x = 0;
228                must_scroll = true;
229            }
230        }
231
232        if (must_scroll)
233            scroll(x, y, VERTICAL | HORIZONTAL);
234
235        return changed;
236    }
237
238    void setViewportSize() {
239        clip.xSetBounds(MARGIN, MARGIN,
240                width - (2*MARGIN)  - vsbSpace,
241                height - (2*MARGIN) - hsbSpace);
242    }
243
244    public void setUnitIncrement(Adjustable adj, int u) {
245        if (adj.getOrientation() == Adjustable.VERTICAL) {
246            vsb.setUnitIncrement(u);
247        } else {
248            // HORIZONTAL
249            hsb.setUnitIncrement(u);
250        }
251    }
252
253    public void setValue(Adjustable adj, int v) {
254        if (adj.getOrientation() == Adjustable.VERTICAL) {
255            scroll(-1, v, VERTICAL);
256        } else {
257            // HORIZONTAL
258            scroll(v, -1, HORIZONTAL);
259        }
260    }
261
262    public void setScrollPosition(int x, int y) {
263        scroll(x, y, VERTICAL | HORIZONTAL);
264    }
265
266    void scroll(int x, int y, int flag) {
267        scroll(x, y, flag, AdjustmentEvent.TRACK);
268    }
269
270    /**
271     * Scroll the contents to position x, y
272     */
273    @SuppressWarnings("deprecation")
274    void scroll(int x, int y, int flag, int type) {
275        checkSecurity();
276        ScrollPane sp = (ScrollPane)target;
277        Component c = getScrollChild();
278        if (c == null) {
279            return;
280        }
281        int sx, sy;
282        Color colors[] = getGUIcolors();
283
284        if (sp.getScrollbarDisplayPolicy() == ScrollPane.SCROLLBARS_NEVER) {
285            sx = -x;
286            sy = -y;
287        } else {
288            Point p = c.location();
289            sx = p.x;
290            sy = p.y;
291
292            if ((flag & HORIZONTAL) != 0) {
293                hsb.setValue(Math.min(x, hsb.getMaximum()-hsb.getVisibleAmount()));
294                ScrollPaneAdjustable hadj = (ScrollPaneAdjustable)sp.getHAdjustable();
295                setAdjustableValue(hadj, hsb.getValue(), type);
296                sx = -(hsb.getValue());
297                Graphics g = getGraphics();
298                if (g != null) {
299                    try {
300                        paintHorScrollbar(g, colors, true);
301                    } finally {
302                        g.dispose();
303                    }
304                }
305            }
306            if ((flag & VERTICAL) != 0) {
307                vsb.setValue(Math.min(y, vsb.getMaximum() - vsb.getVisibleAmount()));
308                ScrollPaneAdjustable vadj = (ScrollPaneAdjustable)sp.getVAdjustable();
309                setAdjustableValue(vadj, vsb.getValue(), type);
310                sy = -(vsb.getValue());
311                Graphics g = getGraphics();
312                if (g != null) {
313                    try {
314                        paintVerScrollbar(g, colors, true);
315                    } finally {
316                        g.dispose();
317                    }
318                }
319            }
320        }
321        c.move(sx, sy);
322    }
323
324    private void setAdjustableValue(final ScrollPaneAdjustable adj, final int value,
325                            final int type) {
326        AWTAccessor.getScrollPaneAdjustableAccessor().setTypedValue(adj, value,
327                                                                    type);
328    }
329    @Override
330    void paintPeer(final Graphics g) {
331        final Color[] colors = getGUIcolors();
332        g.setColor(colors[BACKGROUND_COLOR]);
333        final int h = height - hsbSpace;
334        final int w = width - vsbSpace;
335        g.fillRect(0, 0, w, h);
336        // paint rectangular region between scrollbars
337        g.fillRect(w, h, vsbSpace, hsbSpace);
338        if (MARGIN > 0) {
339            draw3DRect(g, colors, 0, 0, w - 1, h - 1, false);
340        }
341        paintScrollBars(g, colors);
342    }
343    private void paintScrollBars(Graphics g, Color[] colors) {
344        if (vsbSpace > 0) {
345            paintVerScrollbar(g, colors, true);
346            // paint the whole scrollbar
347        }
348
349        if (hsbSpace > 0) {
350            paintHorScrollbar(g, colors, true);
351            // paint the whole scrollbar
352        }
353    }
354    void repaintScrollBars() {
355        Graphics g = getGraphics();
356        Color colors[] = getGUIcolors();
357        if (g != null) {
358            try {
359                paintScrollBars(g, colors);
360            } finally {
361                g.dispose();
362            }
363        }
364    }
365    public void repaintScrollbarRequest(XScrollbar sb) {
366        Graphics g = getGraphics();
367        Color colors[] = getGUIcolors();
368        if (g != null) {
369            try {
370                if (sb == vsb) {
371                    paintVerScrollbar(g, colors, true);
372                } else if (sb == hsb) {
373                    paintHorScrollbar(g, colors, true);
374                }
375            } finally {
376                g.dispose();
377            }
378        }
379    }
380    public void handleEvent(java.awt.AWTEvent e) {
381        super.handleEvent(e);
382
383        int id = e.getID();
384        switch(id) {
385            case PaintEvent.PAINT:
386            case PaintEvent.UPDATE:
387                repaintScrollBars();
388                break;
389        }
390    }
391
392
393    /**
394     * Paint the horizontal scrollbar to the screen
395     *
396     * @param g the graphics context to draw into
397     * @param colors the colors used to draw the scrollbar
398     * @param paintAll paint the whole scrollbar if true, just the thumb if false
399     */
400    void paintHorScrollbar(Graphics g, Color colors[], boolean paintAll) {
401        if (hsbSpace <= 0) {
402            return;
403        }
404        Graphics ng = g.create();
405        g.setColor(colors[BACKGROUND_COLOR]);
406
407        // SCROLLBAR is the height of scrollbar area
408        // but the actual scrollbar is SCROLLBAR-SPACE high;
409        // the rest must be filled with background color
410        int w = width - vsbSpace - (2*MARGIN);
411        g.fillRect(MARGIN, height-SCROLLBAR, w, SPACE);
412        g.fillRect(0, height-SCROLLBAR, MARGIN, SCROLLBAR);
413        g.fillRect(MARGIN + w, height-SCROLLBAR, MARGIN, SCROLLBAR);
414
415        try {
416            ng.translate(MARGIN, height - (SCROLLBAR - SPACE));
417            hsb.paint(ng, colors, paintAll);
418        }
419        finally {
420            ng.dispose();
421        }
422
423
424    }
425
426
427
428
429    /**
430     * Paint the vertical scrollbar to the screen
431     *
432     * @param g the graphics context to draw into
433     * @param colors the colors used to draw the scrollbar
434     * @param paintAll paint the whole scrollbar if true, just the thumb if false
435     */
436    void paintVerScrollbar(Graphics g, Color colors[], boolean paintAll) {
437        if (vsbSpace <= 0) {
438            return;
439        }
440        Graphics ng = g.create();
441        g.setColor(colors[BACKGROUND_COLOR]);
442
443        // SCROLLBAR is the width of scrollbar area
444        // but the actual scrollbar is SCROLLBAR-SPACE wide;
445        // the rest must be filled with background color
446        int h = height - hsbSpace - (2*MARGIN);
447        g.fillRect(width-SCROLLBAR, MARGIN, SPACE, h);
448        g.fillRect(width-SCROLLBAR, 0, SCROLLBAR, MARGIN);
449        g.fillRect(width-SCROLLBAR, MARGIN+h, SCROLLBAR, MARGIN);
450
451        try {
452            ng.translate(width - (SCROLLBAR - SPACE), MARGIN);
453            vsb.paint(ng, colors, paintAll);
454        }
455        finally {
456            ng.dispose();
457        }
458    }
459
460    /**
461     *
462     * @see java.awt.event.MouseEvent
463     * MouseEvent.MOUSE_CLICKED
464     * MouseEvent.MOUSE_PRESSED
465     * MouseEvent.MOUSE_RELEASED
466     * MouseEvent.MOUSE_MOVED
467     * MouseEvent.MOUSE_ENTERED
468     * MouseEvent.MOUSE_EXITED
469     * MouseEvent.MOUSE_DRAGGED
470     */
471    @SuppressWarnings("deprecation")
472    public void handleJavaMouseEvent( MouseEvent mouseEvent ) {
473        super.handleJavaMouseEvent(mouseEvent);
474        int modifiers = mouseEvent.getModifiers();
475        int id = mouseEvent.getID();
476        int x = mouseEvent.getX();
477        int y = mouseEvent.getY();
478
479
480        //        super.handleMouseEvent(mouseEvent);
481
482        if ((modifiers & InputEvent.BUTTON1_MASK) == 0) {
483            return;
484        }
485
486        switch (id) {
487            case MouseEvent.MOUSE_PRESSED:
488                if (inVerticalScrollbar(x,y )) {
489                    active = VERTICAL;
490                    int h = height - hsbSpace - (2*MARGIN);
491                    vsb.handleMouseEvent(id,modifiers,x - (width - SCROLLBAR + SPACE),y-MARGIN);
492                } else if (inHorizontalScrollbar(x, y) ) {
493                    active = HORIZONTAL;
494                    int w = width - 2*MARGIN - vsbSpace;
495                    hsb.handleMouseEvent(id,modifiers,x-MARGIN,y-(height - SCROLLBAR + SPACE));
496                }
497                break;
498
499                // On mouse up, pass the event through to the scrollbar to stop
500                // scrolling. The x & y passed do not matter.
501            case MouseEvent.MOUSE_RELEASED:
502                //     winReleaseCursorFocus();
503                if (active == VERTICAL) {
504                    vsb.handleMouseEvent(id,modifiers,x,y);
505                } else if (active == HORIZONTAL) {
506                    hsb.handleMouseEvent(id,modifiers,x,y);
507                }
508                break;
509
510
511            case MouseEvent.MOUSE_DRAGGED:
512                if ((active == VERTICAL)) {
513                    int h = height - 2*MARGIN - hsbSpace;
514                    vsb.handleMouseEvent(id,modifiers,x-(width - SCROLLBAR + SPACE),y-MARGIN);
515                } else if ((active == HORIZONTAL)) {
516                    int w = width - 2*MARGIN - vsbSpace;
517                    hsb.handleMouseEvent(id,modifiers,x-MARGIN,y-(height - SCROLLBAR + SPACE));
518                }
519                break;
520        }
521    }
522
523    /**
524     * return value from the scrollbar
525     */
526    public void notifyValue(XScrollbar obj, int type, int v, boolean isAdjusting) {
527        if (obj == vsb) {
528            scroll(-1, v, VERTICAL, type);
529        } else if ((XHorizontalScrollbar)obj == hsb) {
530            scroll(v, -1, HORIZONTAL, type);
531        }
532    }
533
534    /**
535     * return true if the x and y position is in the verticalscrollbar
536     */
537    boolean inVerticalScrollbar(int x, int y) {
538        if (vsbSpace <= 0) {
539            return false;
540        }
541        int h = height - MARGIN - hsbSpace;
542        return (x >= width - (SCROLLBAR - SPACE)) && (x < width) && (y >= MARGIN) && (y < h);
543    }
544
545    /**
546     * return true if the x and y position is in the horizontal scrollbar
547     */
548    boolean inHorizontalScrollbar(int x, int y) {
549        if (hsbSpace <= 0) {
550            return false;
551        }
552        int w = width - MARGIN - vsbSpace;
553        return (x >= MARGIN) && (x < w) && (y >= height - (SCROLLBAR - SPACE)) && (y < height);
554    }
555
556    private Component getScrollChild() {
557        ScrollPane sp = (ScrollPane)target;
558        Component child = null;
559        try {
560            child = sp.getComponent(0);
561        } catch (ArrayIndexOutOfBoundsException e) {
562            // do nothing.  in this case we return null
563        }
564        return child;
565    }
566
567    int vval;
568    int hval;
569    int vmax;
570    int hmax;
571    /*
572     * Print the native component by rendering the Motif look ourselves.
573     * ToDo(aim): needs to query native motif for more accurate size and
574     * color information.
575     */
576    @SuppressWarnings("deprecation")
577    public void print(Graphics g) {
578        ScrollPane sp = (ScrollPane)target;
579        Dimension d = sp.size();
580        Color bg = sp.getBackground();
581        Color fg = sp.getForeground();
582        Point p = sp.getScrollPosition();
583        Component c = getScrollChild();
584        Dimension cd;
585        if (c != null) {
586            cd = c.size();
587        } else {
588            cd = new Dimension(0, 0);
589        }
590        int sbDisplay = sp.getScrollbarDisplayPolicy();
591        int vvis, hvis, vmin, hmin, vmax, hmax, vval, hval;
592
593        switch (sbDisplay) {
594            case ScrollPane.SCROLLBARS_NEVER:
595                hsbSpace = vsbSpace = 0;
596                break;
597            case ScrollPane.SCROLLBARS_ALWAYS:
598                hsbSpace = vsbSpace = SCROLLBAR;
599                break;
600            case ScrollPane.SCROLLBARS_AS_NEEDED:
601                hsbSpace = (cd.width <= (d.width - 2*MARGIN)? 0 : SCROLLBAR);
602                vsbSpace = (cd.height <= (d.height - 2*MARGIN)? 0 : SCROLLBAR);
603
604                if (hsbSpace == 0 && vsbSpace != 0) {
605                    hsbSpace = (cd.width <= (d.width - SCROLLBAR - 2*MARGIN)? 0 : SCROLLBAR);
606                }
607                if (vsbSpace == 0 && hsbSpace != 0) {
608                    vsbSpace = (cd.height <= (d.height - SCROLLBAR - 2*MARGIN)? 0 : SCROLLBAR);
609                }
610        }
611
612        vvis = hvis = vmin = hmin = vmax = hmax = vval = hval = 0;
613
614        if (vsbSpace > 0) {
615            vmin = 0;
616            vvis = d.height - (2*MARGIN) - hsbSpace;
617            vmax = Math.max(cd.height - vvis, 0);
618            vval = p.y;
619        }
620        if (hsbSpace > 0) {
621            hmin = 0;
622            hvis = d.width - (2*MARGIN) - vsbSpace;
623            hmax = Math.max(cd.width - hvis, 0);
624            hval = p.x;
625        }
626
627        // need to be careful to add the margins back in here because
628        // we're drawing the margin border, after all!
629        int w = d.width - vsbSpace;
630        int h = d.height - hsbSpace;
631
632        g.setColor(bg);
633        g.fillRect(0, 0, d.width, d.height);
634
635        if (hsbSpace > 0) {
636            int sbw = d.width - vsbSpace;
637            g.fillRect(1, d.height - SCROLLBAR - 3, sbw - 1, SCROLLBAR - 3);
638            Graphics ng = g.create();
639            try {
640                ng.translate(0, d.height - (SCROLLBAR - 2));
641                drawScrollbar(ng, bg, SCROLLBAR - 2, sbw,
642                        hmin, hmax, hval, hvis, true);
643            } finally {
644                ng.dispose();
645            }
646        }
647        if (vsbSpace > 0) {
648            int sbh = d.height - hsbSpace;
649            g.fillRect(d.width - SCROLLBAR - 3, 1, SCROLLBAR - 3, sbh - 1);
650            Graphics ng = g.create();
651            try {
652                ng.translate(d.width - (SCROLLBAR - 2), 0);
653                drawScrollbar(ng, bg, SCROLLBAR - 2, sbh,
654                        vmin, vmax, vval, vvis, false);
655            } finally {
656                ng.dispose();
657            }
658        }
659
660        draw3DRect(g, bg, 0, 0, w - 1, h - 1, false);
661
662        target.print(g);
663        sp.printComponents(g);
664    }
665
666}
667