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&trade;
  54  * has been added to the <code>java.beans</code> package.
  55  * Please see {@link java.beans.XMLEncoder}.
  56  *
  57  * @author Hans Muller
  58  */
  59 @SuppressWarnings("serial") // Same-version serialization only
  60 public class ViewportLayout implements LayoutManager, Serializable
  61 {
  62     // Single instance used by JViewport.
  63     static ViewportLayout SHARED_INSTANCE = new ViewportLayout();
  64 
  65     /**
  66      * Adds the specified component to the layout. Not used by this class.
  67      * @param name the name of the component
  68      * @param c the the component to be added
  69      */
  70     public void addLayoutComponent(String name, Component c) { }
  71 
  72     /**
  73      * Removes the specified component from the layout. Not used by
  74      * this class.
  75      * @param c the component to remove
  76      */
  77     public void removeLayoutComponent(Component c) { }
  78 
  79 
  80     /**
  81      * Returns the preferred dimensions for this layout given the components
  82      * in the specified target container.
  83      * @param parent the component which needs to be laid out
  84      * @return a <code>Dimension</code> object containing the
  85      *          preferred dimensions
  86      * @see #minimumLayoutSize
  87      */
  88     public Dimension preferredLayoutSize(Container parent) {
  89         Component view = ((JViewport)parent).getView();
  90         if (view == null) {
  91             return new Dimension(0, 0);
  92         }
  93         else if (view instanceof Scrollable) {
  94             return ((Scrollable)view).getPreferredScrollableViewportSize();
  95         }
  96         else {
  97             return view.getPreferredSize();
  98         }
  99     }
 100 
 101 
 102     /**
 103      * Returns the minimum dimensions needed to layout the components
 104      * contained in the specified target container.
 105      *
 106      * @param parent the component which needs to be laid out
 107      * @return a <code>Dimension</code> object containing the minimum
 108      *          dimensions
 109      * @see #preferredLayoutSize
 110      */
 111     public Dimension minimumLayoutSize(Container parent) {
 112         return new Dimension(4, 4);
 113     }
 114 
 115 
 116     /**
 117      * Called by the AWT when the specified container needs to be laid out.
 118      *
 119      * @param parent  the container to lay out
 120      *
 121      * @throws AWTError if the target isn't the container specified to the
 122      *                      <code>BoxLayout</code> constructor
 123      */
 124     public void layoutContainer(Container parent)
 125     {
 126         JViewport vp = (JViewport)parent;
 127         Component view = vp.getView();
 128         Scrollable scrollableView = null;
 129 
 130         if (view == null) {
 131             return;
 132         }
 133         else if (view instanceof Scrollable) {
 134             scrollableView = (Scrollable) view;
 135         }
 136 
 137         /* All of the dimensions below are in view coordinates, except
 138          * vpSize which we're converting.
 139          */
 140 
 141         Insets insets = vp.getInsets();
 142         Dimension viewPrefSize = view.getPreferredSize();
 143         Dimension vpSize = vp.getSize();
 144         Dimension extentSize = vp.toViewCoordinates(vpSize);
 145         Dimension viewSize = new Dimension(viewPrefSize);
 146 
 147         if (scrollableView != null) {
 148             if (scrollableView.getScrollableTracksViewportWidth()) {
 149                 viewSize.width = vpSize.width;
 150             }
 151             if (scrollableView.getScrollableTracksViewportHeight()) {
 152                 viewSize.height = vpSize.height;
 153             }
 154         }
 155 
 156         Point viewPosition = vp.getViewPosition();
 157 
 158         /* If the new viewport size would leave empty space to the
 159          * right of the view, right justify the view or left justify
 160          * the view when the width of the view is smaller than the
 161          * container.
 162          */
 163         if (scrollableView == null ||
 164             vp.getParent() == null ||
 165             vp.getParent().getComponentOrientation().isLeftToRight()) {
 166             if ((viewPosition.x + extentSize.width) > viewSize.width) {
 167                 viewPosition.x = Math.max(0, viewSize.width - extentSize.width);
 168             }
 169         } else {
 170             if (extentSize.width > viewSize.width) {
 171                 viewPosition.x = viewSize.width - extentSize.width;
 172             } else {
 173                 viewPosition.x = Math.max(0, Math.min(viewSize.width - extentSize.width, viewPosition.x));
 174             }
 175         }
 176 
 177         /* If the new viewport size would leave empty space below the
 178          * view, bottom justify the view or top justify the view when
 179          * the height of the view is smaller than the container.
 180          */
 181         if ((viewPosition.y + extentSize.height) > viewSize.height) {
 182             viewPosition.y = Math.max(0, viewSize.height - extentSize.height);
 183         }
 184 
 185         /* If we haven't been advised about how the viewports size
 186          * should change wrt to the viewport, i.e. if the view isn't
 187          * an instance of Scrollable, then adjust the views size as follows.
 188          *
 189          * If the origin of the view is showing and the viewport is
 190          * bigger than the views preferred size, then make the view
 191          * the same size as the viewport.
 192          */
 193         if (scrollableView == null) {
 194             if ((viewPosition.x == 0) && (vpSize.width > viewPrefSize.width)) {
 195                 viewSize.width = vpSize.width;
 196             }
 197             if ((viewPosition.y == 0) && (vpSize.height > viewPrefSize.height)) {
 198                 viewSize.height = vpSize.height;
 199             }
 200         }
 201         vp.setViewPosition(viewPosition);
 202         vp.setViewSize(viewSize);
 203     }
 204 }