001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Dmitry A. Durnev
019: * @version $Revision$
020: */package org.apache.harmony.awt;
021:
022: import java.awt.Adjustable;
023: import java.awt.Component;
024: import java.awt.Dimension;
025: import java.awt.Insets;
026: import java.awt.Point;
027: import java.awt.Rectangle;
028: import java.awt.event.AdjustmentEvent;
029: import java.awt.event.AdjustmentListener;
030: import java.awt.event.ComponentAdapter;
031: import java.awt.event.ComponentEvent;
032: import java.awt.event.MouseWheelEvent;
033: import java.awt.event.MouseWheelListener;
034:
035: /**
036: * Implements scrolling behavior for any container which
037: * has up to 1 child and up to 2 scrollbars[and implements
038: * Scrollable interface], for example ScrollPane.
039: * Listens to adjustment events and mouse wheel events
040: * and updates the scrollable object in response.
041: * @see org.apache.harmony.awt.Scrollable
042: */
043: public class ScrollStateController extends ComponentAdapter implements
044: AdjustmentListener, MouseWheelListener {
045:
046: private static final int HSCROLLBAR_HEIGHT = 16;
047:
048: private static final int VSCROLLBAR_WIDTH = 16;
049:
050: private final Scrollable scrollable;
051:
052: private final Component component;
053:
054: private final Adjustable hAdj;
055:
056: private final Adjustable vAdj;
057:
058: public ScrollStateController(Scrollable scrollable) {
059: this .scrollable = scrollable;
060: component = scrollable.getComponent();
061: hAdj = scrollable.getHAdjustable();
062: vAdj = scrollable.getVAdjustable();
063: }
064:
065: /**
066: * Scrolls vertically or horizontally(if scrollable has only
067: * horizontal scrollbar) on mouse wheel move
068: */
069: public void mouseWheelMoved(MouseWheelEvent e) {
070: int type = e.getScrollType();
071: int unitType = MouseWheelEvent.WHEEL_UNIT_SCROLL;
072: Adjustable adj = vAdj;
073: if (!isAdjNeeded(vAdj) && isAdjNeeded(hAdj)) {
074: // scroll horizontally if only horiz scrollbar
075: // is present
076: adj = hAdj;
077: }
078: int incrSize = (type == unitType ? adj.getUnitIncrement() : adj
079: .getBlockIncrement());
080: int scrollAmount = e.getUnitsToScroll() * incrSize;
081:
082: adj.setValue(adj.getValue() + scrollAmount);
083: }
084:
085: /**
086: * Scrolls component on adjustment events from
087: * scrollbars, repaints scrollable viewport
088: */
089: public void adjustmentValueChanged(AdjustmentEvent e) {
090: // set scrolled component's position here:
091: Adjustable srcAdj = e.getAdjustable();
092: int val = e.getValue();
093:
094: Insets ins = scrollable.getInsets();
095: Point loc = scrollable.getLocation();
096: if (srcAdj == hAdj) {
097: loc.x = ins.left - val;
098: }
099: if (srcAdj == vAdj) {
100: loc.y = ins.top - val;
101: }
102: scrollable.setLocation(loc);
103: int hSize = scrollable.getWidth() - getHrzInsets(ins);
104: int vSize = scrollable.getHeight() - getVrtInsets(ins);
105: Rectangle r = new Rectangle(ins.left, ins.top, hSize, vSize);
106: scrollable.doRepaint(r); // repaint only client area
107: }
108:
109: /**
110: * Recalculates internal scrollable layout
111: * on component resize
112: */
113: @Override
114: public void componentResized(ComponentEvent e) {
115: if (!component.isDisplayable()) {
116: return;
117: }
118: scrollable.doRepaint();
119: }
120:
121: /**
122: * Adds vertical scrollbar width to the specified horizontal
123: * insets
124: * @param insets
125: * @return sum of left & right insets & vertical scrollbar width
126: */
127: private int getHrzInsets(Insets insets) {
128: return (insets != null) ? insets.left + insets.right
129: + scrollable.getAdjustableWidth() : 0;
130: }
131:
132: /**
133: * Adds horizontal scrollbar height to the specified vertical
134: * insets
135: * @param insets
136: * @return sum of top & bottom insets & horizontal scrollbar height
137: */
138: private int getVrtInsets(Insets insets) {
139: return (insets != null) ? insets.top + insets.bottom
140: + scrollable.getAdjustableHeight() : 0;
141: }
142:
143: /**
144: * Sets scrollbars bounds, values & increments
145: */
146: public void layoutScrollbars() {
147: boolean hAdjNeeded = isAdjNeeded(hAdj);
148: boolean vAdjNeeded = isAdjNeeded(vAdj);
149: Insets insets = scrollable.getInsets();
150: Dimension size = scrollable.getSize();
151:
152: setAdjBounds(hAdjNeeded, vAdjNeeded);
153:
154: if (hAdj != null) {
155: int hSize = scrollable.getWidth() - getHrzInsets(insets);
156: int hGap = insets.left + insets.right;
157: int hVis = getVisFromSize(hSize, hGap);
158: int newHMax = Math.max(size.width, hVis);
159: scrollable.setAdjustableSizes(hAdj, hVis, 0, newHMax);
160: hAdj.setBlockIncrement(getBlockIncrFromVis(hVis));
161: }
162:
163: if (vAdj != null) {
164: int vSize = scrollable.getHeight() - getVrtInsets(insets);
165: int vGap = insets.bottom + insets.top;
166: int vVis = getVisFromSize(vSize, vGap);
167: int newVMax = Math.max(size.height, vVis);
168: scrollable.setAdjustableSizes(vAdj, vVis, 0, newVMax);
169: vAdj.setBlockIncrement(getBlockIncrFromVis(vVis));
170: }
171: }
172:
173: /**
174: * Calculates & sets scrollbars bounds
175: *
176: * @param hAdjNeeded true if it's necessary to layout horizontal scrollbar
177: * @param vAdjNeeded true if it's necessary to layout vertical scrollbar
178: */
179: private void setAdjBounds(boolean hAdjNeeded, boolean vAdjNeeded) {
180: Rectangle hRect = new Rectangle();
181: Rectangle vRect = new Rectangle();
182: int spHeight = scrollable.getHeight();
183: int spWidth = scrollable.getWidth();
184: Insets ins = scrollable.getInsets();
185:
186: if (hAdjNeeded) {
187: hRect.height = HSCROLLBAR_HEIGHT;
188: int vWidth = vAdjNeeded ? VSCROLLBAR_WIDTH : 0;
189: hRect.width = spWidth - vWidth - (ins.left + ins.right);
190: hRect.y = spHeight - hRect.height - ins.bottom;
191: hRect.x = ins.left;
192: }
193: scrollable.setAdjustableBounds(hAdj, hRect);
194: if (vAdjNeeded) {
195: int hHeight = hAdjNeeded ? HSCROLLBAR_HEIGHT : 0;
196: vRect.height = spHeight - hHeight - (ins.top + ins.bottom);
197: vRect.width = VSCROLLBAR_WIDTH;
198: vRect.x = spWidth - vRect.width - ins.right;
199: vRect.y = ins.top;
200: }
201: scrollable.setAdjustableBounds(vAdj, vRect);
202: }
203:
204: /**
205: * Calculates block increment given visible amount of scrollbar
206: * @param vis visible amount
207: * @return block increment size
208: */
209: private int getBlockIncrFromVis(int vis) {
210: return Math.max(1, vis * 9 / 10);
211: }
212:
213: /**
214: * Calculates visible amount given size(length) of scrollbar's
215: * viewport
216: * @param size viewport size
217: * @param gap insets(without scrollbar area) size
218: * @return visible amount
219: */
220: private int getVisFromSize(int size, int gap) {
221: return Math.max(1, size - gap);
222: }
223:
224: /**
225: * Determines whether a specified adjustable should
226: * be displayed using scrollbar display policy
227: * @param adj scrollbar
228: * @return true if scrollbar should be displayed, false otherwise
229: */
230: private boolean isAdjNeeded(Adjustable adj) {
231: if (adj == null) {
232: return false;
233: }
234: switch (scrollable.getAdjustableMode(adj)) {
235: case Scrollable.ALWAYS:
236: return true;
237: case Scrollable.NEVER:
238: return false;
239: case Scrollable.AS_NEEDED:
240: return calculateNeeded(adj);
241: case Scrollable.HORIZONTAL_ONLY:
242: return adj.getOrientation() == Adjustable.HORIZONTAL;
243: case Scrollable.VERTICAL_ONLY:
244: return adj.getOrientation() == Adjustable.VERTICAL;
245: }
246: return true;
247: }
248:
249: /**
250: * Only for Scrollable.AS_NEEDED scrollbar display policy:
251: * determines if the specified scrollbar should be visible.
252: * Scrollbar is shown only if child component can be scrolled in
253: * corresponding direction, i. e. the size of the child exceeds
254: * container size, and there's enough space for
255: * scrollbar itself.
256: * @param adj scrollbar
257: * @return true if scrollbar is needed, false otherwise
258: */
259: private boolean calculateNeeded(Adjustable adj) {
260: Insets ins = scrollable.getInsets();
261: Component comp = scrollable.getComponent();
262: final int GAP = ins.left + ins.right;
263: boolean isVertical = (adj.getOrientation() == Adjustable.VERTICAL);
264: Dimension viewSize = comp.getSize();
265: viewSize.width -= getHrzInsets(ins);
266: viewSize.height -= getVrtInsets(ins);
267: int viewWidth = viewSize.width;
268: int viewHeight = viewSize.height;
269: int spOtherSize = isVertical ? viewWidth : viewHeight;
270: int spSize = isVertical ? viewHeight : viewWidth;
271:
272: int compSize = 0;
273: int adjSize = (isVertical ? scrollable.getAdjustableWidth()
274: : scrollable.getAdjustableHeight());
275: if (comp != null) {
276: Dimension prefSize = scrollable.getSize();
277: compSize = isVertical ? prefSize.height : prefSize.width;
278: }
279: return ((spSize < compSize) && (spOtherSize > adjSize + GAP));
280: }
281: }
|