1 /*
   2  * Copyright (c) 1997, 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 
  26 package javax.swing;
  27 
  28 import java.awt.AWTError;
  29 import java.awt.LayoutManager;
  30 import java.awt.Component;
  31 import java.awt.Container;
  32 import java.awt.Rectangle;
  33 import java.awt.Point;
  34 import java.awt.Dimension;
  35 import java.awt.Insets;
  36 import java.io.Serializable;
  37 
  38 /**
  39  * The default layout manager for <code>JViewport</code>.
  40  * <code>ViewportLayout</code> defines
  41  * a policy for layout that should be useful for most applications.
  42  * The viewport makes its view the same size as the viewport,
  43  * however it will not make the view smaller than its minimum size.
  44  * As the viewport grows the view is kept bottom justified until
  45  * the entire view is visible, subsequently the view is kept top
  46  * justified.
  47  * <p>
  48  * <strong>Warning:</strong>
  49  * Serialized objects of this class will not be compatible with
  50  * future Swing releases. The current serialization support is
  51  * appropriate for short term storage or RMI between applications running
  52  * the same version of Swing.  As of 1.4, support for long term storage
  53  * of all JavaBeans
  54  * has been added to the <code>java.beans</code> package.
  55  * Please see {@link java.beans.XMLEncoder}.
  56  *
  57  * @author Hans Muller
  58  * @since 1.2
  59  */
  60 @SuppressWarnings("serial") // Same-version serialization only
  61 public class ViewportLayout implements LayoutManager, Serializable
  62 {
  63     // Single instance used by JViewport.
  64     static ViewportLayout SHARED_INSTANCE = new ViewportLayout();
  65 
  66     /**
  67      * Adds the specified component to the layout. Not used by this class.
  68      * @param name the name of the component
  69      * @param c the component to be added
  70      */
  71     public void addLayoutComponent(String name, Component c) { }
  72 
  73     /**
  74      * Removes the specified component from the layout. Not used by
  75      * this class.
  76      * @param c the component to remove
  77      */
  78     public void removeLayoutComponent(Component c) { }
  79 
  80 
  81     /**
  82      * Returns the preferred dimensions for this layout given the components
  83      * in the specified target container.
  84      * @param parent the component which needs to be laid out
  85      * @return a <code>Dimension</code> object containing the
  86      *          preferred dimensions
  87      * @see #minimumLayoutSize
  88      */
  89     public Dimension preferredLayoutSize(Container parent) {
  90         Component view = ((JViewport)parent).getView();
  91         if (view == null) {
  92             return new Dimension(0, 0);
  93         }
  94         else if (view instanceof Scrollable) {
  95             return ((Scrollable)view).getPreferredScrollableViewportSize();
  96         }
  97         else {
  98             return view.getPreferredSize();
  99         }
 100     }
 101 
 102 
 103     /**
 104      * Returns the minimum dimensions needed to layout the components
 105      * contained in the specified target container.
 106      *
 107      * @param parent the component which needs to be laid out
 108      * @return a <code>Dimension</code> object containing the minimum
 109      *          dimensions
 110      * @see #preferredLayoutSize
 111      */
 112     public Dimension minimumLayoutSize(Container parent) {
 113         return new Dimension(4, 4);
 114     }
 115 
 116 
 117     /**
 118      * Called by the AWT when the specified container needs to be laid out.
 119      *
 120      * @param parent  the container to lay out
 121      *
 122      * @throws AWTError if the target isn't the container specified to the
 123      *                      <code>BoxLayout</code> constructor
 124      */
 125     public void layoutContainer(Container parent)
 126     {
 127         JViewport vp = (JViewport)parent;
 128         Component view = vp.getView();
 129         Scrollable scrollableView = null;
 130 
 131         if (view == null) {
 132             return;
 133         }
 134         else if (view instanceof Scrollable) {
 135             scrollableView = (Scrollable) view;
 136         }
 137 
 138         /* All of the dimensions below are in view coordinates, except
 139          * vpSize which we're converting.
 140          */
 141 
 142         Insets insets = vp.getInsets();
 143         Dimension viewPrefSize = view.getPreferredSize();
 144         Dimension vpSize = vp.getSize();
 145         Dimension extentSize = vp.toViewCoordinates(vpSize);
 146         Dimension viewSize = new Dimension(viewPrefSize);
 147 
 148         if (scrollableView != null) {
 149             if (scrollableView.getScrollableTracksViewportWidth()) {
 150                 viewSize.width = vpSize.width;
 151             }
 152             if (scrollableView.getScrollableTracksViewportHeight()) {
 153                 viewSize.height = vpSize.height;
 154             }
 155         }
 156 
 157         Point viewPosition = vp.getViewPosition();
 158 
 159         /* If the new viewport size would leave empty space to the
 160          * right of the view, right justify the view or left justify
 161          * the view when the width of the view is smaller than the
 162          * container.
 163          */
 164         if (scrollableView == null ||
 165             vp.getParent() == null ||
 166             vp.getParent().getComponentOrientation().isLeftToRight()) {
 167             if ((viewPosition.x + extentSize.width) > viewSize.width) {
 168                 viewPosition.x = Math.max(0, viewSize.width - extentSize.width);
 169             }
 170         } else {
 171             if (extentSize.width > viewSize.width) {
 172                 viewPosition.x = viewSize.width - extentSize.width;
 173             } else {
 174                 viewPosition.x = Math.max(0, Math.min(viewSize.width - extentSize.width, viewPosition.x));
 175             }
 176         }
 177 
 178         /* If the new viewport size would leave empty space below the
 179          * view, bottom justify the view or top justify the view when
 180          * the height of the view is smaller than the container.
 181          */
 182         if ((viewPosition.y + extentSize.height) > viewSize.height) {
 183             viewPosition.y = Math.max(0, viewSize.height - extentSize.height);
 184         }
 185 
 186         /* If we haven't been advised about how the viewports size
 187          * should change wrt to the viewport, i.e. if the view isn't
 188          * an instance of Scrollable, then adjust the views size as follows.
 189          *
 190          * If the origin of the view is showing and the viewport is
 191          * bigger than the views preferred size, then make the view
 192          * the same size as the viewport.
 193          */
 194         if (scrollableView == null) {
 195             if ((viewPosition.x == 0) && (vpSize.width > viewPrefSize.width)) {
 196                 viewSize.width = vpSize.width;
 197             }
 198             if ((viewPosition.y == 0) && (vpSize.height > viewPrefSize.height)) {
 199                 viewSize.height = vpSize.height;
 200             }
 201         }
 202         vp.setViewPosition(viewPosition);
 203         vp.setViewSize(viewSize);
 204     }
 205 }