| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to you under the Apache License, |
| * Version 2.0 (the "License"); you may not use this file except in |
| * compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.pivot.wtk; |
| |
| import org.apache.pivot.collections.Sequence; |
| import org.apache.pivot.util.ListenerList; |
| |
| /** |
| * Container that provides a scrollable view of a component, with optional fixed |
| * row and column headers. <p> The single component to be scrolled will |
| * typically be a {@link Container} and should be specified by the |
| * {@link #setView setView()} method (the "view" property). So, even then though |
| * this class is a {@link Container}, you should not add components to it via |
| * the {@link #add add()} method. |
| */ |
| public class ScrollPane extends Viewport { |
| /** |
| * Enumeration defining when to show a scroll bar, and if not showing, |
| * whether to constrain the pane's content to the size of the ScrollPane, or |
| * to let the content be shown at its unconstrained size. |
| */ |
| public enum ScrollBarPolicy { |
| /** |
| * Show the scroll bar if the pane's content exceeds the size of the |
| * pane in the relevant dimension. Does not have any effect on the |
| * layout of the content. This is the default setting. |
| */ |
| AUTO, |
| /** |
| * Never show the scroll bar, and don't affect the layout of the pane's |
| * content. |
| */ |
| NEVER, |
| /** |
| * Always show the scroll bar, and don't affect the layout of the pane's |
| * content. |
| */ |
| ALWAYS, |
| /** |
| * Do not show the scroll bar, and cause the pane's content to be laid |
| * out to exactly fill the available space in the relevant dimension of |
| * the pane. |
| */ |
| FILL, |
| /** |
| * Show the scroll bar if the pane's content exceeds the size of the |
| * pane in the relevant dimension; if it does not, act like |
| * <code>FILL</code>, omitting the scroll bar and causing the pane's |
| * content to fill the available space in the relevant dimension of the |
| * pane. |
| */ |
| FILL_TO_CAPACITY |
| } |
| |
| private static class ScrollPaneListenerList extends WTKListenerList<ScrollPaneListener> |
| implements ScrollPaneListener { |
| |
| @Override |
| public void horizontalScrollBarPolicyChanged(ScrollPane scrollPane, |
| ScrollBarPolicy previousHorizontalScrollBarPolicy) { |
| for (ScrollPaneListener listener : this) { |
| listener.horizontalScrollBarPolicyChanged(scrollPane, |
| previousHorizontalScrollBarPolicy); |
| } |
| } |
| |
| @Override |
| public void verticalScrollBarPolicyChanged(ScrollPane scrollPane, |
| ScrollBarPolicy previousVerticalScrollBarPolicy) { |
| for (ScrollPaneListener listener : this) { |
| listener.verticalScrollBarPolicyChanged(scrollPane, previousVerticalScrollBarPolicy); |
| } |
| } |
| |
| @Override |
| public void rowHeaderChanged(ScrollPane scrollPane, Component previousRowHeader) { |
| for (ScrollPaneListener listener : this) { |
| listener.rowHeaderChanged(scrollPane, previousRowHeader); |
| } |
| } |
| |
| @Override |
| public void columnHeaderChanged(ScrollPane scrollPane, Component previousColumnHeader) { |
| for (ScrollPaneListener listener : this) { |
| listener.columnHeaderChanged(scrollPane, previousColumnHeader); |
| } |
| } |
| |
| @Override |
| public void cornerChanged(ScrollPane scrollPane, Component previousCorner) { |
| for (ScrollPaneListener listener : this) { |
| listener.cornerChanged(scrollPane, previousCorner); |
| } |
| } |
| } |
| |
| /** |
| * Component class representing the components that will get placed in the |
| * corners of a <tt>ScrollPane</tt>. Skins will instantiate these components |
| * as needed when unfilled corners are introduced by a row header or column |
| * header. |
| */ |
| public static class Corner extends Component { |
| /** |
| * Enumeration defining placement values for scroll pane corners. |
| */ |
| public enum Placement { |
| TOP_LEFT, BOTTOM_LEFT, BOTTOM_RIGHT, TOP_RIGHT; |
| } |
| |
| private Placement placement; |
| |
| public Corner(Placement placement) { |
| if (placement == null) { |
| throw new IllegalArgumentException("Placement is null."); |
| } |
| |
| this.placement = placement; |
| |
| installSkin(Corner.class); |
| } |
| |
| public Placement getPlacement() { |
| return placement; |
| } |
| } |
| |
| private ScrollBarPolicy horizontalScrollBarPolicy; |
| private ScrollBarPolicy verticalScrollBarPolicy; |
| private Component rowHeader; |
| private Component columnHeader; |
| private Component corner; |
| private ScrollPaneListenerList scrollPaneListeners = new ScrollPaneListenerList(); |
| |
| public ScrollPane() { |
| this(ScrollBarPolicy.AUTO, ScrollBarPolicy.AUTO); |
| } |
| |
| public ScrollPane(ScrollBarPolicy horizontalScrollBarPolicy, |
| ScrollBarPolicy verticalScrollBarPolicy) { |
| super(); |
| |
| if (horizontalScrollBarPolicy == null) { |
| throw new IllegalArgumentException("horizontalScrollBarPolicy is null"); |
| } |
| |
| if (verticalScrollBarPolicy == null) { |
| throw new IllegalArgumentException("verticalScrollBarPolicy is null"); |
| } |
| |
| this.horizontalScrollBarPolicy = horizontalScrollBarPolicy; |
| this.verticalScrollBarPolicy = verticalScrollBarPolicy; |
| |
| installSkin(ScrollPane.class); |
| } |
| |
| public ScrollBarPolicy getHorizontalScrollBarPolicy() { |
| return horizontalScrollBarPolicy; |
| } |
| |
| public void setHorizontalScrollBarPolicy(ScrollBarPolicy horizontalScrollBarPolicy) { |
| if (horizontalScrollBarPolicy == null) { |
| throw new IllegalArgumentException("horizontalScrollBarPolicy is null"); |
| } |
| |
| ScrollBarPolicy previousHorizontalScrollBarPolicy = this.horizontalScrollBarPolicy; |
| |
| if (horizontalScrollBarPolicy != previousHorizontalScrollBarPolicy) { |
| this.horizontalScrollBarPolicy = horizontalScrollBarPolicy; |
| scrollPaneListeners.horizontalScrollBarPolicyChanged(this, |
| previousHorizontalScrollBarPolicy); |
| } |
| } |
| |
| public ScrollBarPolicy getVerticalScrollBarPolicy() { |
| return verticalScrollBarPolicy; |
| } |
| |
| public void setVerticalScrollBarPolicy(ScrollBarPolicy verticalScrollBarPolicy) { |
| if (verticalScrollBarPolicy == null) { |
| throw new IllegalArgumentException("verticalScrollBarPolicy is null"); |
| } |
| |
| ScrollBarPolicy previousVerticalScrollBarPolicy = this.verticalScrollBarPolicy; |
| |
| if (verticalScrollBarPolicy != previousVerticalScrollBarPolicy) { |
| this.verticalScrollBarPolicy = verticalScrollBarPolicy; |
| scrollPaneListeners.verticalScrollBarPolicyChanged(this, |
| previousVerticalScrollBarPolicy); |
| } |
| } |
| |
| public Component getRowHeader() { |
| return rowHeader; |
| } |
| |
| public void setRowHeader(Component rowHeader) { |
| Component previousRowHeader = this.rowHeader; |
| |
| if (rowHeader != previousRowHeader) { |
| // Remove any previous rowHeader component |
| if (previousRowHeader != null) { |
| remove(previousRowHeader); |
| } |
| |
| this.rowHeader = null; |
| |
| if (rowHeader != null) { |
| if (rowHeader.getParent() != null) { |
| throw new IllegalArgumentException("Component already has a parent."); |
| } |
| |
| int insertionIndex = 0; |
| |
| if (getView() != null) { |
| insertionIndex++; |
| } |
| |
| // Add the component |
| insert(rowHeader, insertionIndex); |
| } |
| |
| this.rowHeader = rowHeader; |
| |
| scrollPaneListeners.rowHeaderChanged(this, previousRowHeader); |
| } |
| } |
| |
| public Component getColumnHeader() { |
| return columnHeader; |
| } |
| |
| public void setColumnHeader(Component columnHeader) { |
| Component previousColumnHeader = this.columnHeader; |
| |
| if (columnHeader != previousColumnHeader) { |
| // Remove any previous columnHeader component |
| if (previousColumnHeader != null) { |
| remove(previousColumnHeader); |
| } |
| |
| this.columnHeader = null; |
| |
| if (columnHeader != null) { |
| int insertionIndex = 0; |
| |
| if (getView() != null) { |
| insertionIndex++; |
| } |
| |
| // Add the component |
| insert(columnHeader, insertionIndex); |
| } |
| |
| this.columnHeader = columnHeader; |
| |
| scrollPaneListeners.columnHeaderChanged(this, previousColumnHeader); |
| } |
| } |
| |
| public Component getCorner() { |
| return corner; |
| } |
| |
| public void setCorner(Component corner) { |
| Component previousCorner = this.corner; |
| |
| if (corner != this.corner) { |
| // Remove any previous corner component |
| if (previousCorner != null) { |
| remove(previousCorner); |
| } |
| |
| this.corner = null; |
| |
| if (corner != null) { |
| int insertionIndex = 0; |
| |
| if (getView() != null) { |
| insertionIndex++; |
| } |
| |
| if (rowHeader != null) { |
| insertionIndex++; |
| } |
| |
| if (columnHeader != null) { |
| insertionIndex++; |
| } |
| |
| // Add the component |
| insert(corner, insertionIndex); |
| } |
| |
| this.corner = corner; |
| |
| scrollPaneListeners.cornerChanged(this, previousCorner); |
| } |
| } |
| |
| @Override |
| public Sequence<Component> remove(int index, int count) { |
| for (int i = index, n = index + count; i < n; i++) { |
| Component component = get(i); |
| if (component == rowHeader || component == columnHeader || component == corner) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| // Call the base method to remove the components |
| return super.remove(index, count); |
| } |
| |
| public ListenerList<ScrollPaneListener> getScrollPaneListeners() { |
| return scrollPaneListeners; |
| } |
| } |