History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/text/BoxView.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/text/BoxView.java+1188−0
@@ -0,0 +1,1188 @@
1+/*
2+ * Copyright (c) 1997, 2006, 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+package javax.swing.text;
26+
27+import java.io.PrintStream;
28+import java.util.Vector;
29+import java.awt.*;
30+import javax.swing.event.DocumentEvent;
31+import javax.swing.SizeRequirements;
32+
33+/**
34+ * A view that arranges its children into a box shape by tiling
35+ * its children along an axis. The box is somewhat like that
36+ * found in TeX where there is alignment of the
37+ * children, flexibility of the children is considered, etc.
38+ * This is a building block that might be useful to represent
39+ * things like a collection of lines, paragraphs,
40+ * lists, columns, pages, etc. The axis along which the children are tiled is
41+ * considered the major axis. The orthoginal axis is the minor axis.
42+ * <p>
43+ * Layout for each axis is handled separately by the methods
44+ * <code>layoutMajorAxis</code> and <code>layoutMinorAxis</code>.
45+ * Subclasses can change the layout algorithm by
46+ * reimplementing these methods. These methods will be called
47+ * as necessary depending upon whether or not there is cached
48+ * layout information and the cache is considered
49+ * valid. These methods are typically called if the given size
50+ * along the axis changes, or if <code>layoutChanged</code> is
51+ * called to force an updated layout. The <code>layoutChanged</code>
52+ * method invalidates cached layout information, if there is any.
53+ * The requirements published to the parent view are calculated by
54+ * the methods <code>calculateMajorAxisRequirements</code>
55+ * and <code>calculateMinorAxisRequirements</code>.
56+ * If the layout algorithm is changed, these methods will
57+ * likely need to be reimplemented.
58+ *
59+ * @author Timothy Prinzing
60+ */
61+public class BoxView extends CompositeView {
62+
63+ /**
64+ * Constructs a <code>BoxView</code>.
65+ *
66+ * @param elem the element this view is responsible for
67+ * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
68+ */
69+ public BoxView(Element elem, int axis) {
70+ super(elem);
71+ tempRect = new Rectangle();
72+ this.majorAxis = axis;
73+
74+ majorOffsets = new int[0];
75+ majorSpans = new int[0];
76+ majorReqValid = false;
77+ majorAllocValid = false;
78+ minorOffsets = new int[0];
79+ minorSpans = new int[0];
80+ minorReqValid = false;
81+ minorAllocValid = false;
82+ }
83+
84+ /**
85+ * Fetches the tile axis property. This is the axis along which
86+ * the child views are tiled.
87+ *
88+ * @return the major axis of the box, either
89+ * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
90+ *
91+ * @since 1.3
92+ */
93+ public int getAxis() {
94+ return majorAxis;
95+ }
96+
97+ /**
98+ * Sets the tile axis property. This is the axis along which
99+ * the child views are tiled.
100+ *
101+ * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
102+ *
103+ * @since 1.3
104+ */
105+ public void setAxis(int axis) {
106+ boolean axisChanged = (axis != majorAxis);
107+ majorAxis = axis;
108+ if (axisChanged) {
109+ preferenceChanged(null, true, true);
110+ }
111+ }
112+
113+ /**
114+ * Invalidates the layout along an axis. This happens
115+ * automatically if the preferences have changed for
116+ * any of the child views. In some cases the layout
117+ * may need to be recalculated when the preferences
118+ * have not changed. The layout can be marked as
119+ * invalid by calling this method. The layout will
120+ * be updated the next time the <code>setSize</code> method
121+ * is called on this view (typically in paint).
122+ *
123+ * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
124+ *
125+ * @since 1.3
126+ */
127+ public void layoutChanged(int axis) {
128+ if (axis == majorAxis) {
129+ majorAllocValid = false;
130+ } else {
131+ minorAllocValid = false;
132+ }
133+ }
134+
135+ /**
136+ * Determines if the layout is valid along the given axis.
137+ *
138+ * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
139+ *
140+ * @since 1.4
141+ */
142+ protected boolean isLayoutValid(int axis) {
143+ if (axis == majorAxis) {
144+ return majorAllocValid;
145+ } else {
146+ return minorAllocValid;
147+ }
148+ }
149+
150+ /**
151+ * Paints a child. By default
152+ * that is all it does, but a subclass can use this to paint
153+ * things relative to the child.
154+ *
155+ * @param g the graphics context
156+ * @param alloc the allocated region to paint into
157+ * @param index the child index, >= 0 && < getViewCount()
158+ */
159+ protected void paintChild(Graphics g, Rectangle alloc, int index) {
160+ View child = getView(index);
161+ child.paint(g, alloc);
162+ }
163+
164+ // --- View methods ---------------------------------------------
165+
166+ /**
167+ * Invalidates the layout and resizes the cache of
168+ * requests/allocations. The child allocations can still
169+ * be accessed for the old layout, but the new children
170+ * will have an offset and span of 0.
171+ *
172+ * @param index the starting index into the child views to insert
173+ * the new views; this should be a value >= 0 and <= getViewCount
174+ * @param length the number of existing child views to remove;
175+ * This should be a value >= 0 and <= (getViewCount() - offset)
176+ * @param elems the child views to add; this value can be
177+ * <code>null</code>to indicate no children are being added
178+ * (useful to remove)
179+ */
180+ public void replace(int index, int length, View[] elems) {
181+ super.replace(index, length, elems);
182+
183+ // invalidate cache
184+ int nInserted = (elems != null) ? elems.length : 0;
185+ majorOffsets = updateLayoutArray(majorOffsets, index, nInserted);
186+ majorSpans = updateLayoutArray(majorSpans, index, nInserted);
187+ majorReqValid = false;
188+ majorAllocValid = false;
189+ minorOffsets = updateLayoutArray(minorOffsets, index, nInserted);
190+ minorSpans = updateLayoutArray(minorSpans, index, nInserted);
191+ minorReqValid = false;
192+ minorAllocValid = false;
193+ }
194+
195+ /**
196+ * Resizes the given layout array to match the new number of
197+ * child views. The current number of child views are used to
198+ * produce the new array. The contents of the old array are
199+ * inserted into the new array at the appropriate places so that
200+ * the old layout information is transferred to the new array.
201+ *
202+ * @param oldArray the original layout array
203+ * @param offset location where new views will be inserted
204+ * @param nInserted the number of child views being inserted;
205+ * therefore the number of blank spaces to leave in the
206+ * new array at location <code>offset</code>
207+ * @return the new layout array
208+ */
209+ int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
210+ int n = getViewCount();
211+ int[] newArray = new int[n];
212+
213+ System.arraycopy(oldArray, 0, newArray, 0, offset);
214+ System.arraycopy(oldArray, offset,
215+ newArray, offset + nInserted, n - nInserted - offset);
216+ return newArray;
217+ }
218+
219+ /**
220+ * Forwards the given <code>DocumentEvent</code> to the child views
221+ * that need to be notified of the change to the model.
222+ * If a child changed its requirements and the allocation
223+ * was valid prior to forwarding the portion of the box
224+ * from the starting child to the end of the box will
225+ * be repainted.
226+ *
227+ * @param ec changes to the element this view is responsible
228+ * for (may be <code>null</code> if there were no changes)
229+ * @param e the change information from the associated document
230+ * @param a the current allocation of the view
231+ * @param f the factory to use to rebuild if the view has children
232+ * @see #insertUpdate
233+ * @see #removeUpdate
234+ * @see #changedUpdate
235+ * @since 1.3
236+ */
237+ protected void forwardUpdate(DocumentEvent.ElementChange ec,
238+ DocumentEvent e, Shape a, ViewFactory f) {
239+ boolean wasValid = isLayoutValid(majorAxis);
240+ super.forwardUpdate(ec, e, a, f);
241+
242+ // determine if a repaint is needed
243+ if (wasValid && (! isLayoutValid(majorAxis))) {
244+ // Repaint is needed because one of the tiled children
245+ // have changed their span along the major axis. If there
246+ // is a hosting component and an allocated shape we repaint.
247+ Component c = getContainer();
248+ if ((a != null) && (c != null)) {
249+ int pos = e.getOffset();
250+ int index = getViewIndexAtPosition(pos);
251+ Rectangle alloc = getInsideAllocation(a);
252+ if (majorAxis == X_AXIS) {
253+ alloc.x += majorOffsets[index];
254+ alloc.width -= majorOffsets[index];
255+ } else {
256+ alloc.y += minorOffsets[index];
257+ alloc.height -= minorOffsets[index];
258+ }
259+ c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
260+ }
261+ }
262+ }
263+
264+ /**
265+ * This is called by a child to indicate its
266+ * preferred span has changed. This is implemented to
267+ * throw away cached layout information so that new
268+ * calculations will be done the next time the children
269+ * need an allocation.
270+ *
271+ * @param child the child view
272+ * @param width true if the width preference should change
273+ * @param height true if the height preference should change
274+ */
275+ public void preferenceChanged(View child, boolean width, boolean height) {
276+ boolean majorChanged = (majorAxis == X_AXIS) ? width : height;
277+ boolean minorChanged = (majorAxis == X_AXIS) ? height : width;
278+ if (majorChanged) {
279+ majorReqValid = false;
280+ majorAllocValid = false;
281+ }
282+ if (minorChanged) {
283+ minorReqValid = false;
284+ minorAllocValid = false;
285+ }
286+ super.preferenceChanged(child, width, height);
287+ }
288+
289+ /**
290+ * Gets the resize weight. A value of 0 or less is not resizable.
291+ *
292+ * @param axis may be either <code>View.X_AXIS</code> or
293+ * <code>View.Y_AXIS</code>
294+ * @return the weight
295+ * @exception IllegalArgumentException for an invalid axis
296+ */
297+ public int getResizeWeight(int axis) {
298+ checkRequests(axis);
299+ if (axis == majorAxis) {
300+ if ((majorRequest.preferred != majorRequest.minimum) ||
301+ (majorRequest.preferred != majorRequest.maximum)) {
302+ return 1;
303+ }
304+ } else {
305+ if ((minorRequest.preferred != minorRequest.minimum) ||
306+ (minorRequest.preferred != minorRequest.maximum)) {
307+ return 1;
308+ }
309+ }
310+ return 0;
311+ }
312+
313+ /**
314+ * Sets the size of the view along an axis. This should cause
315+ * layout of the view along the given axis.
316+ *
317+ * @param axis may be either <code>View.X_AXIS</code> or
318+ * <code>View.Y_AXIS</code>
319+ * @param span the span to layout to >= 0
320+ */
321+ void setSpanOnAxis(int axis, float span) {
322+ if (axis == majorAxis) {
323+ if (majorSpan != (int) span) {
324+ majorAllocValid = false;
325+ }
326+ if (! majorAllocValid) {
327+ // layout the major axis
328+ majorSpan = (int) span;
329+ checkRequests(majorAxis);
330+ layoutMajorAxis(majorSpan, axis, majorOffsets, majorSpans);
331+ majorAllocValid = true;
332+
333+ // flush changes to the children
334+ updateChildSizes();
335+ }
336+ } else {
337+ if (((int) span) != minorSpan) {
338+ minorAllocValid = false;
339+ }
340+ if (! minorAllocValid) {
341+ // layout the minor axis
342+ minorSpan = (int) span;
343+ checkRequests(axis);
344+ layoutMinorAxis(minorSpan, axis, minorOffsets, minorSpans);
345+ minorAllocValid = true;
346+
347+ // flush changes to the children
348+ updateChildSizes();
349+ }
350+ }
351+ }
352+
353+ /**
354+ * Propagates the current allocations to the child views.
355+ */
356+ void updateChildSizes() {
357+ int n = getViewCount();
358+ if (majorAxis == X_AXIS) {
359+ for (int i = 0; i < n; i++) {
360+ View v = getView(i);
361+ v.setSize((float) majorSpans[i], (float) minorSpans[i]);
362+ }
363+ } else {
364+ for (int i = 0; i < n; i++) {
365+ View v = getView(i);
366+ v.setSize((float) minorSpans[i], (float) majorSpans[i]);
367+ }
368+ }
369+ }
370+
371+ /**
372+ * Returns the size of the view along an axis. This is implemented
373+ * to return zero.
374+ *
375+ * @param axis may be either <code>View.X_AXIS</code> or
376+ * <code>View.Y_AXIS</code>
377+ * @return the current span of the view along the given axis, >= 0
378+ */
379+ float getSpanOnAxis(int axis) {
380+ if (axis == majorAxis) {
381+ return majorSpan;
382+ } else {
383+ return minorSpan;
384+ }
385+ }
386+
387+ /**
388+ * Sets the size of the view. This should cause
389+ * layout of the view if the view caches any layout
390+ * information. This is implemented to call the
391+ * layout method with the sizes inside of the insets.
392+ *
393+ * @param width the width >= 0
394+ * @param height the height >= 0
395+ */
396+ public void setSize(float width, float height) {
397+ layout(Math.max(0, (int)(width - getLeftInset() - getRightInset())),
398+ Math.max(0, (int)(height - getTopInset() - getBottomInset())));
399+ }
400+
401+ /**
402+ * Renders the <code>BoxView</code> using the given
403+ * rendering surface and area
404+ * on that surface. Only the children that intersect
405+ * the clip bounds of the given <code>Graphics</code>
406+ * will be rendered.
407+ *
408+ * @param g the rendering surface to use
409+ * @param allocation the allocated region to render into
410+ * @see View#paint
411+ */
412+ public void paint(Graphics g, Shape allocation) {
413+ Rectangle alloc = (allocation instanceof Rectangle) ?
414+ (Rectangle)allocation : allocation.getBounds();
415+ int n = getViewCount();
416+ int x = alloc.x + getLeftInset();
417+ int y = alloc.y + getTopInset();
418+ Rectangle clip = g.getClipBounds();
419+ for (int i = 0; i < n; i++) {
420+ tempRect.x = x + getOffset(X_AXIS, i);
421+ tempRect.y = y + getOffset(Y_AXIS, i);
422+ tempRect.width = getSpan(X_AXIS, i);
423+ tempRect.height = getSpan(Y_AXIS, i);
424+ int trx0 = tempRect.x, trx1 = trx0 + tempRect.width;
425+ int try0 = tempRect.y, try1 = try0 + tempRect.height;
426+ int crx0 = clip.x, crx1 = crx0 + clip.width;
427+ int cry0 = clip.y, cry1 = cry0 + clip.height;
428+ // We should paint views that intersect with clipping region
429+ // even if the intersection has no inside points (is a line).
430+ // This is needed for supporting views that have zero width, like
431+ // views that contain only combining marks.
432+ if ((trx1 >= crx0) && (try1 >= cry0) && (crx1 >= trx0) && (cry1 >= try0)) {
433+ paintChild(g, tempRect, i);
434+ }
435+ }
436+ }
437+
438+ /**
439+ * Fetches the allocation for the given child view.
440+ * This enables finding out where various views
441+ * are located. This is implemented to return
442+ * <code>null</code> if the layout is invalid,
443+ * otherwise the superclass behavior is executed.
444+ *
445+ * @param index the index of the child, >= 0 && < getViewCount()
446+ * @param a the allocation to this view
447+ * @return the allocation to the child; or <code>null</code>
448+ * if <code>a</code> is <code>null</code>;
449+ * or <code>null</code> if the layout is invalid
450+ */
451+ public Shape getChildAllocation(int index, Shape a) {
452+ if (a != null) {
453+ Shape ca = super.getChildAllocation(index, a);
454+ if ((ca != null) && (! isAllocationValid())) {
455+ // The child allocation may not have been set yet.
456+ Rectangle r = (ca instanceof Rectangle) ?
457+ (Rectangle) ca : ca.getBounds();
458+ if ((r.width == 0) && (r.height == 0)) {
459+ return null;
460+ }
461+ }
462+ return ca;
463+ }
464+ return null;
465+ }
466+
467+ /**
468+ * Provides a mapping from the document model coordinate space
469+ * to the coordinate space of the view mapped to it. This makes
470+ * sure the allocation is valid before calling the superclass.
471+ *
472+ * @param pos the position to convert >= 0
473+ * @param a the allocated region to render into
474+ * @return the bounding box of the given position
475+ * @exception BadLocationException if the given position does
476+ * not represent a valid location in the associated document
477+ * @see View#modelToView
478+ */
479+ public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
480+ if (! isAllocationValid()) {
481+ Rectangle alloc = a.getBounds();
482+ setSize(alloc.width, alloc.height);
483+ }
484+ return super.modelToView(pos, a, b);
485+ }
486+
487+ /**
488+ * Provides a mapping from the view coordinate space to the logical
489+ * coordinate space of the model.
490+ *
491+ * @param x x coordinate of the view location to convert >= 0
492+ * @param y y coordinate of the view location to convert >= 0
493+ * @param a the allocated region to render into
494+ * @return the location within the model that best represents the
495+ * given point in the view >= 0
496+ * @see View#viewToModel
497+ */
498+ public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
499+ if (! isAllocationValid()) {
500+ Rectangle alloc = a.getBounds();
501+ setSize(alloc.width, alloc.height);
502+ }
503+ return super.viewToModel(x, y, a, bias);
504+ }
505+
506+ /**
507+ * Determines the desired alignment for this view along an
508+ * axis. This is implemented to give the total alignment
509+ * needed to position the children with the alignment points
510+ * lined up along the axis orthoginal to the axis that is
511+ * being tiled. The axis being tiled will request to be
512+ * centered (i.e. 0.5f).
513+ *
514+ * @param axis may be either <code>View.X_AXIS</code>
515+ * or <code>View.Y_AXIS</code>
516+ * @return the desired alignment >= 0.0f && <= 1.0f; this should
517+ * be a value between 0.0 and 1.0 where 0 indicates alignment at the
518+ * origin and 1.0 indicates alignment to the full span
519+ * away from the origin; an alignment of 0.5 would be the
520+ * center of the view
521+ * @exception IllegalArgumentException for an invalid axis
522+ */
523+ public float getAlignment(int axis) {
524+ checkRequests(axis);
525+ if (axis == majorAxis) {
526+ return majorRequest.alignment;
527+ } else {
528+ return minorRequest.alignment;
529+ }
530+ }
531+
532+ /**
533+ * Determines the preferred span for this view along an
534+ * axis.
535+ *
536+ * @param axis may be either <code>View.X_AXIS</code>
537+ * or <code>View.Y_AXIS</code>
538+ * @return the span the view would like to be rendered into >= 0;
539+ * typically the view is told to render into the span
540+ * that is returned, although there is no guarantee;
541+ * the parent may choose to resize or break the view
542+ * @exception IllegalArgumentException for an invalid axis type
543+ */
544+ public float getPreferredSpan(int axis) {
545+ checkRequests(axis);
546+ float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
547+ getTopInset() + getBottomInset();
548+ if (axis == majorAxis) {
549+ return ((float)majorRequest.preferred) + marginSpan;
550+ } else {
551+ return ((float)minorRequest.preferred) + marginSpan;
552+ }
553+ }
554+
555+ /**
556+ * Determines the minimum span for this view along an
557+ * axis.
558+ *
559+ * @param axis may be either <code>View.X_AXIS</code>
560+ * or <code>View.Y_AXIS</code>
561+ * @return the span the view would like to be rendered into >= 0;
562+ * typically the view is told to render into the span
563+ * that is returned, although there is no guarantee;
564+ * the parent may choose to resize or break the view
565+ * @exception IllegalArgumentException for an invalid axis type
566+ */
567+ public float getMinimumSpan(int axis) {
568+ checkRequests(axis);
569+ float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
570+ getTopInset() + getBottomInset();
571+ if (axis == majorAxis) {
572+ return ((float)majorRequest.minimum) + marginSpan;
573+ } else {
574+ return ((float)minorRequest.minimum) + marginSpan;
575+ }
576+ }
577+
578+ /**
579+ * Determines the maximum span for this view along an
580+ * axis.
581+ *
582+ * @param axis may be either <code>View.X_AXIS</code>
583+ * or <code>View.Y_AXIS</code>
584+ * @return the span the view would like to be rendered into >= 0;
585+ * typically the view is told to render into the span
586+ * that is returned, although there is no guarantee;
587+ * the parent may choose to resize or break the view
588+ * @exception IllegalArgumentException for an invalid axis type
589+ */
590+ public float getMaximumSpan(int axis) {
591+ checkRequests(axis);
592+ float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
593+ getTopInset() + getBottomInset();
594+ if (axis == majorAxis) {
595+ return ((float)majorRequest.maximum) + marginSpan;
596+ } else {
597+ return ((float)minorRequest.maximum) + marginSpan;
598+ }
599+ }
600+
601+ // --- local methods ----------------------------------------------------
602+
603+ /**
604+ * Are the allocations for the children still
605+ * valid?
606+ *
607+ * @return true if allocations still valid
608+ */
609+ protected boolean isAllocationValid() {
610+ return (majorAllocValid && minorAllocValid);
611+ }
612+
613+ /**
614+ * Determines if a point falls before an allocated region.
615+ *
616+ * @param x the X coordinate >= 0
617+ * @param y the Y coordinate >= 0
618+ * @param innerAlloc the allocated region; this is the area
619+ * inside of the insets
620+ * @return true if the point lies before the region else false
621+ */
622+ protected boolean isBefore(int x, int y, Rectangle innerAlloc) {
623+ if (majorAxis == View.X_AXIS) {
624+ return (x < innerAlloc.x);
625+ } else {
626+ return (y < innerAlloc.y);
627+ }
628+ }
629+
630+ /**
631+ * Determines if a point falls after an allocated region.
632+ *
633+ * @param x the X coordinate >= 0
634+ * @param y the Y coordinate >= 0
635+ * @param innerAlloc the allocated region; this is the area
636+ * inside of the insets
637+ * @return true if the point lies after the region else false
638+ */
639+ protected boolean isAfter(int x, int y, Rectangle innerAlloc) {
640+ if (majorAxis == View.X_AXIS) {
641+ return (x > (innerAlloc.width + innerAlloc.x));
642+ } else {
643+ return (y > (innerAlloc.height + innerAlloc.y));
644+ }
645+ }
646+
647+ /**
648+ * Fetches the child view at the given coordinates.
649+ *
650+ * @param x the X coordinate >= 0
651+ * @param y the Y coordinate >= 0
652+ * @param alloc the parents inner allocation on entry, which should
653+ * be changed to the childs allocation on exit
654+ * @return the view
655+ */
656+ protected View getViewAtPoint(int x, int y, Rectangle alloc) {
657+ int n = getViewCount();
658+ if (majorAxis == View.X_AXIS) {
659+ if (x < (alloc.x + majorOffsets[0])) {
660+ childAllocation(0, alloc);
661+ return getView(0);
662+ }
663+ for (int i = 0; i < n; i++) {
664+ if (x < (alloc.x + majorOffsets[i])) {
665+ childAllocation(i - 1, alloc);
666+ return getView(i - 1);
667+ }
668+ }
669+ childAllocation(n - 1, alloc);
670+ return getView(n - 1);
671+ } else {
672+ if (y < (alloc.y + majorOffsets[0])) {
673+ childAllocation(0, alloc);
674+ return getView(0);
675+ }
676+ for (int i = 0; i < n; i++) {
677+ if (y < (alloc.y + majorOffsets[i])) {
678+ childAllocation(i - 1, alloc);
679+ return getView(i - 1);
680+ }
681+ }
682+ childAllocation(n - 1, alloc);
683+ return getView(n - 1);
684+ }
685+ }
686+
687+ /**
688+ * Allocates a region for a child view.
689+ *
690+ * @param index the index of the child view to
691+ * allocate, >= 0 && < getViewCount()
692+ * @param alloc the allocated region
693+ */
694+ protected void childAllocation(int index, Rectangle alloc) {
695+ alloc.x += getOffset(X_AXIS, index);
696+ alloc.y += getOffset(Y_AXIS, index);
697+ alloc.width = getSpan(X_AXIS, index);
698+ alloc.height = getSpan(Y_AXIS, index);
699+ }
700+
701+ /**
702+ * Perform layout on the box
703+ *
704+ * @param width the width (inside of the insets) >= 0
705+ * @param height the height (inside of the insets) >= 0
706+ */
707+ protected void layout(int width, int height) {
708+ setSpanOnAxis(X_AXIS, width);
709+ setSpanOnAxis(Y_AXIS, height);
710+ }
711+
712+ /**
713+ * Returns the current width of the box. This is the width that
714+ * it was last allocated.
715+ * @return the current width of the box
716+ */
717+ public int getWidth() {
718+ int span;
719+ if (majorAxis == X_AXIS) {
720+ span = majorSpan;
721+ } else {
722+ span = minorSpan;
723+ }
724+ span += getLeftInset() - getRightInset();
725+ return span;
726+ }
727+
728+ /**
729+ * Returns the current height of the box. This is the height that
730+ * it was last allocated.
731+ * @return the current height of the box
732+ */
733+ public int getHeight() {
734+ int span;
735+ if (majorAxis == Y_AXIS) {
736+ span = majorSpan;
737+ } else {
738+ span = minorSpan;
739+ }
740+ span += getTopInset() - getBottomInset();
741+ return span;
742+ }
743+
744+ /**
745+ * Performs layout for the major axis of the box (i.e. the
746+ * axis that it represents). The results of the layout (the
747+ * offset and span for each children) are placed in the given
748+ * arrays which represent the allocations to the children
749+ * along the major axis.
750+ *
751+ * @param targetSpan the total span given to the view, which
752+ * would be used to layout the children
753+ * @param axis the axis being layed out
754+ * @param offsets the offsets from the origin of the view for
755+ * each of the child views; this is a return value and is
756+ * filled in by the implementation of this method
757+ * @param spans the span of each child view; this is a return
758+ * value and is filled in by the implementation of this method
759+ */
760+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
761+ /*
762+ * first pass, calculate the preferred sizes
763+ * and the flexibility to adjust the sizes.
764+ */
765+ long preferred = 0;
766+ int n = getViewCount();
767+ for (int i = 0; i < n; i++) {
768+ View v = getView(i);
769+ spans[i] = (int) v.getPreferredSpan(axis);
770+ preferred += spans[i];
771+ }
772+
773+ /*
774+ * Second pass, expand or contract by as much as possible to reach
775+ * the target span.
776+ */
777+
778+ // determine the adjustment to be made
779+ long desiredAdjustment = targetSpan - preferred;
780+ float adjustmentFactor = 0.0f;
781+ int[] diffs = null;
782+
783+ if (desiredAdjustment != 0) {
784+ long totalSpan = 0;
785+ diffs = new int[n];
786+ for (int i = 0; i < n; i++) {
787+ View v = getView(i);
788+ int tmp;
789+ if (desiredAdjustment < 0) {
790+ tmp = (int)v.getMinimumSpan(axis);
791+ diffs[i] = spans[i] - tmp;
792+ } else {
793+ tmp = (int)v.getMaximumSpan(axis);
794+ diffs[i] = tmp - spans[i];
795+ }
796+ totalSpan += tmp;
797+ }
798+
799+ float maximumAdjustment = Math.abs(totalSpan - preferred);
800+ adjustmentFactor = desiredAdjustment / maximumAdjustment;
801+ adjustmentFactor = Math.min(adjustmentFactor, 1.0f);
802+ adjustmentFactor = Math.max(adjustmentFactor, -1.0f);
803+ }
804+
805+ // make the adjustments
806+ int totalOffset = 0;
807+ for (int i = 0; i < n; i++) {
808+ offsets[i] = totalOffset;
809+ if (desiredAdjustment != 0) {
810+ float adjF = adjustmentFactor * diffs[i];
811+ spans[i] += Math.round(adjF);
812+ }
813+ totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
814+ }
815+ }
816+
817+ /**
818+ * Performs layout for the minor axis of the box (i.e. the
819+ * axis orthoginal to the axis that it represents). The results
820+ * of the layout (the offset and span for each children) are
821+ * placed in the given arrays which represent the allocations to
822+ * the children along the minor axis.
823+ *
824+ * @param targetSpan the total span given to the view, which
825+ * would be used to layout the children
826+ * @param axis the axis being layed out
827+ * @param offsets the offsets from the origin of the view for
828+ * each of the child views; this is a return value and is
829+ * filled in by the implementation of this method
830+ * @param spans the span of each child view; this is a return
831+ * value and is filled in by the implementation of this method
832+ */
833+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
834+ int n = getViewCount();
835+ for (int i = 0; i < n; i++) {
836+ View v = getView(i);
837+ int max = (int) v.getMaximumSpan(axis);
838+ if (max < targetSpan) {
839+ // can't make the child this wide, align it
840+ float align = v.getAlignment(axis);
841+ offsets[i] = (int) ((targetSpan - max) * align);
842+ spans[i] = max;
843+ } else {
844+ // make it the target width, or as small as it can get.
845+ int min = (int)v.getMinimumSpan(axis);
846+ offsets[i] = 0;
847+ spans[i] = Math.max(min, targetSpan);
848+ }
849+ }
850+ }
851+
852+ /**
853+ * Calculates the size requirements for the major axis
854+ * <code>axis</code>.
855+ *
856+ * @param axis the axis being studied
857+ * @param r the <code>SizeRequirements</code> object;
858+ * if <code>null</code> one will be created
859+ * @return the newly initialized <code>SizeRequirements</code> object
860+ * @see javax.swing.SizeRequirements
861+ */
862+ protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
863+ // calculate tiled request
864+ float min = 0;
865+ float pref = 0;
866+ float max = 0;
867+
868+ int n = getViewCount();
869+ for (int i = 0; i < n; i++) {
870+ View v = getView(i);
871+ min += v.getMinimumSpan(axis);
872+ pref += v.getPreferredSpan(axis);
873+ max += v.getMaximumSpan(axis);
874+ }
875+
876+ if (r == null) {
877+ r = new SizeRequirements();
878+ }
879+ r.alignment = 0.5f;
880+ r.minimum = (int) min;
881+ r.preferred = (int) pref;
882+ r.maximum = (int) max;
883+ return r;
884+ }
885+
886+ /**
887+ * Calculates the size requirements for the minor axis
888+ * <code>axis</code>.
889+ *
890+ * @param axis the axis being studied
891+ * @param r the <code>SizeRequirements</code> object;
892+ * if <code>null</code> one will be created
893+ * @return the newly initialized <code>SizeRequirements</code> object
894+ * @see javax.swing.SizeRequirements
895+ */
896+ protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
897+ int min = 0;
898+ long pref = 0;
899+ int max = Integer.MAX_VALUE;
900+ int n = getViewCount();
901+ for (int i = 0; i < n; i++) {
902+ View v = getView(i);
903+ min = Math.max((int) v.getMinimumSpan(axis), min);
904+ pref = Math.max((int) v.getPreferredSpan(axis), pref);
905+ max = Math.max((int) v.getMaximumSpan(axis), max);
906+ }
907+
908+ if (r == null) {
909+ r = new SizeRequirements();
910+ r.alignment = 0.5f;
911+ }
912+ r.preferred = (int) pref;
913+ r.minimum = min;
914+ r.maximum = max;
915+ return r;
916+ }
917+
918+ /**
919+ * Checks the request cache and update if needed.
920+ * @param axis the axis being studied
921+ * @exception IllegalArgumentException if <code>axis</code> is
922+ * neither <code>View.X_AXIS</code> nor <code>View.Y_AXIS</code>
923+ */
924+ void checkRequests(int axis) {
925+ if ((axis != X_AXIS) && (axis != Y_AXIS)) {
926+ throw new IllegalArgumentException("Invalid axis: " + axis);
927+ }
928+ if (axis == majorAxis) {
929+ if (!majorReqValid) {
930+ majorRequest = calculateMajorAxisRequirements(axis,
931+ majorRequest);
932+ majorReqValid = true;
933+ }
934+ } else if (! minorReqValid) {
935+ minorRequest = calculateMinorAxisRequirements(axis, minorRequest);
936+ minorReqValid = true;
937+ }
938+ }
939+
940+ /**
941+ * Computes the location and extent of each child view
942+ * in this <code>BoxView</code> given the <code>targetSpan</code>,
943+ * which is the width (or height) of the region we have to
944+ * work with.
945+ *
946+ * @param targetSpan the total span given to the view, which
947+ * would be used to layout the children
948+ * @param axis the axis being studied, either
949+ * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
950+ * @param offsets an empty array filled by this method with
951+ * values specifying the location of each child view
952+ * @param spans an empty array filled by this method with
953+ * values specifying the extent of each child view
954+ */
955+ protected void baselineLayout(int targetSpan, int axis, int[] offsets, int[] spans) {
956+ int totalAscent = (int)(targetSpan * getAlignment(axis));
957+ int totalDescent = targetSpan - totalAscent;
958+
959+ int n = getViewCount();
960+
961+ for (int i = 0; i < n; i++) {
962+ View v = getView(i);
963+ float align = v.getAlignment(axis);
964+ float viewSpan;
965+
966+ if (v.getResizeWeight(axis) > 0) {
967+ // if resizable then resize to the best fit
968+
969+ // the smallest span possible
970+ float minSpan = v.getMinimumSpan(axis);
971+ // the largest span possible
972+ float maxSpan = v.getMaximumSpan(axis);
973+
974+ if (align == 0.0f) {
975+ // if the alignment is 0 then we need to fit into the descent
976+ viewSpan = Math.max(Math.min(maxSpan, totalDescent), minSpan);
977+ } else if (align == 1.0f) {
978+ // if the alignment is 1 then we need to fit into the ascent
979+ viewSpan = Math.max(Math.min(maxSpan, totalAscent), minSpan);
980+ } else {
981+ // figure out the span that we must fit into
982+ float fitSpan = Math.min(totalAscent / align,
983+ totalDescent / (1.0f - align));
984+ // fit into the calculated span
985+ viewSpan = Math.max(Math.min(maxSpan, fitSpan), minSpan);
986+ }
987+ } else {
988+ // otherwise use the preferred spans
989+ viewSpan = v.getPreferredSpan(axis);
990+ }
991+
992+ offsets[i] = totalAscent - (int)(viewSpan * align);
993+ spans[i] = (int)viewSpan;
994+ }
995+ }
996+
997+ /**
998+ * Calculates the size requirements for this <code>BoxView</code>
999+ * by examining the size of each child view.
1000+ *
1001+ * @param axis the axis being studied
1002+ * @param r the <code>SizeRequirements</code> object;
1003+ * if <code>null</code> one will be created
1004+ * @return the newly initialized <code>SizeRequirements</code> object
1005+ */
1006+ protected SizeRequirements baselineRequirements(int axis, SizeRequirements r) {
1007+ SizeRequirements totalAscent = new SizeRequirements();
1008+ SizeRequirements totalDescent = new SizeRequirements();
1009+
1010+ if (r == null) {
1011+ r = new SizeRequirements();
1012+ }
1013+
1014+ r.alignment = 0.5f;
1015+
1016+ int n = getViewCount();
1017+
1018+ // loop through all children calculating the max of all their ascents and
1019+ // descents at minimum, preferred, and maximum sizes
1020+ for (int i = 0; i < n; i++) {
1021+ View v = getView(i);
1022+ float align = v.getAlignment(axis);
1023+ float span;
1024+ int ascent;
1025+ int descent;
1026+
1027+ // find the maximum of the preferred ascents and descents
1028+ span = v.getPreferredSpan(axis);
1029+ ascent = (int)(align * span);
1030+ descent = (int)(span - ascent);
1031+ totalAscent.preferred = Math.max(ascent, totalAscent.preferred);
1032+ totalDescent.preferred = Math.max(descent, totalDescent.preferred);
1033+
1034+ if (v.getResizeWeight(axis) > 0) {
1035+ // if the view is resizable then do the same for the minimum and
1036+ // maximum ascents and descents
1037+ span = v.getMinimumSpan(axis);
1038+ ascent = (int)(align * span);
1039+ descent = (int)(span - ascent);
1040+ totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
1041+ totalDescent.minimum = Math.max(descent, totalDescent.minimum);
1042+
1043+ span = v.getMaximumSpan(axis);
1044+ ascent = (int)(align * span);
1045+ descent = (int)(span - ascent);
1046+ totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
1047+ totalDescent.maximum = Math.max(descent, totalDescent.maximum);
1048+ } else {
1049+ // otherwise use the preferred
1050+ totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
1051+ totalDescent.minimum = Math.max(descent, totalDescent.minimum);
1052+ totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
1053+ totalDescent.maximum = Math.max(descent, totalDescent.maximum);
1054+ }
1055+ }
1056+
1057+ // we now have an overall preferred, minimum, and maximum ascent and descent
1058+
1059+ // calculate the preferred span as the sum of the preferred ascent and preferred descent
1060+ r.preferred = (int)Math.min((long)totalAscent.preferred + (long)totalDescent.preferred,
1061+ Integer.MAX_VALUE);
1062+
1063+ // calculate the preferred alignment as the preferred ascent divided by the preferred span
1064+ if (r.preferred > 0) {
1065+ r.alignment = (float)totalAscent.preferred / r.preferred;
1066+ }
1067+
1068+
1069+ if (r.alignment == 0.0f) {
1070+ // if the preferred alignment is 0 then the minimum and maximum spans are simply
1071+ // the minimum and maximum descents since there's nothing above the baseline
1072+ r.minimum = totalDescent.minimum;
1073+ r.maximum = totalDescent.maximum;
1074+ } else if (r.alignment == 1.0f) {
1075+ // if the preferred alignment is 1 then the minimum and maximum spans are simply
1076+ // the minimum and maximum ascents since there's nothing below the baseline
1077+ r.minimum = totalAscent.minimum;
1078+ r.maximum = totalAscent.maximum;
1079+ } else {
1080+ // we want to honor the preferred alignment so we calculate two possible minimum
1081+ // span values using 1) the minimum ascent and the alignment, and 2) the minimum
1082+ // descent and the alignment. We'll choose the larger of these two numbers.
1083+ r.minimum = Math.round(Math.max(totalAscent.minimum / r.alignment,
1084+ totalDescent.minimum / (1.0f - r.alignment)));
1085+ // a similar calculation is made for the maximum but we choose the smaller number.
1086+ r.maximum = Math.round(Math.min(totalAscent.maximum / r.alignment,
1087+ totalDescent.maximum / (1.0f - r.alignment)));
1088+ }
1089+
1090+ return r;
1091+ }
1092+
1093+ /**
1094+ * Fetches the offset of a particular child's current layout.
1095+ * @param axis the axis being studied
1096+ * @param childIndex the index of the requested child
1097+ * @return the offset (location) for the specified child
1098+ */
1099+ protected int getOffset(int axis, int childIndex) {
1100+ int[] offsets = (axis == majorAxis) ? majorOffsets : minorOffsets;
1101+ return offsets[childIndex];
1102+ }
1103+
1104+ /**
1105+ * Fetches the span of a particular childs current layout.
1106+ * @param axis the axis being studied
1107+ * @param childIndex the index of the requested child
1108+ * @return the span (width or height) of the specified child
1109+ */
1110+ protected int getSpan(int axis, int childIndex) {
1111+ int[] spans = (axis == majorAxis) ? majorSpans : minorSpans;
1112+ return spans[childIndex];
1113+ }
1114+
1115+ /**
1116+ * Determines in which direction the next view lays.
1117+ * Consider the View at index n. Typically the <code>View</code>s
1118+ * are layed out from left to right, so that the <code>View</code>
1119+ * to the EAST will be at index n + 1, and the <code>View</code>
1120+ * to the WEST will be at index n - 1. In certain situations,
1121+ * such as with bidirectional text, it is possible
1122+ * that the <code>View</code> to EAST is not at index n + 1,
1123+ * but rather at index n - 1, or that the <code>View</code>
1124+ * to the WEST is not at index n - 1, but index n + 1.
1125+ * In this case this method would return true,
1126+ * indicating the <code>View</code>s are layed out in
1127+ * descending order. Otherwise the method would return false
1128+ * indicating the <code>View</code>s are layed out in ascending order.
1129+ * <p>
1130+ * If the receiver is laying its <code>View</code>s along the
1131+ * <code>Y_AXIS</code>, this will will return the value from
1132+ * invoking the same method on the <code>View</code>
1133+ * responsible for rendering <code>position</code> and
1134+ * <code>bias</code>. Otherwise this will return false.
1135+ *
1136+ * @param position position into the model
1137+ * @param bias either <code>Position.Bias.Forward</code> or
1138+ * <code>Position.Bias.Backward</code>
1139+ * @return true if the <code>View</code>s surrounding the
1140+ * <code>View</code> responding for rendering
1141+ * <code>position</code> and <code>bias</code>
1142+ * are layed out in descending order; otherwise false
1143+ */
1144+ protected boolean flipEastAndWestAtEnds(int position,
1145+ Position.Bias bias) {
1146+ if(majorAxis == Y_AXIS) {
1147+ int testPos = (bias == Position.Bias.Backward) ?
1148+ Math.max(0, position - 1) : position;
1149+ int index = getViewIndexAtPosition(testPos);
1150+ if(index != -1) {
1151+ View v = getView(index);
1152+ if(v != null && v instanceof CompositeView) {
1153+ return ((CompositeView)v).flipEastAndWestAtEnds(position,
1154+ bias);
1155+ }
1156+ }
1157+ }
1158+ return false;
1159+ }
1160+
1161+ // --- variables ------------------------------------------------
1162+
1163+ int majorAxis;
1164+
1165+ int majorSpan;
1166+ int minorSpan;
1167+
1168+ /*
1169+ * Request cache
1170+ */
1171+ boolean majorReqValid;
1172+ boolean minorReqValid;
1173+ SizeRequirements majorRequest;
1174+ SizeRequirements minorRequest;
1175+
1176+ /*
1177+ * Allocation cache
1178+ */
1179+ boolean majorAllocValid;
1180+ int[] majorOffsets;
1181+ int[] majorSpans;
1182+ boolean minorAllocValid;
1183+ int[] minorOffsets;
1184+ int[] minorSpans;
1185+
1186+ /** used in paint. */
1187+ Rectangle tempRect;
1188+}