KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > editor > view > GapBoxViewChildren


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.lib.editor.view;
21
22 import java.awt.Component JavaDoc;
23 import java.awt.Graphics JavaDoc;
24 import java.awt.Rectangle JavaDoc;
25 import java.awt.Shape JavaDoc;
26 import javax.swing.text.AbstractDocument JavaDoc;
27 import javax.swing.text.View JavaDoc;
28 import org.netbeans.editor.view.spi.EstimatedSpanView;
29 import org.netbeans.editor.view.spi.LockView;
30 import org.netbeans.editor.view.spi.ViewInsets;
31 import org.netbeans.editor.view.spi.ViewLayoutQueue;
32 import org.netbeans.editor.view.spi.ViewLayoutState;
33
34 //import org.netbeans.spi.lexer.util.GapObjectArray;
35

36 /**
37  * Maintainer of the children of the {@link GapBoxView}.
38  * <br>
39  * It also acts as a runnable task for flushing requirement
40  * changes of the view it works for.
41  *
42  * Besides the current implementation there could be
43  * an implementation for small number of children (e.g. up to 20)
44  * which would not have to use index gap and which would use indexes
45  * &lt;= 127 that could be merged into shorts by pairs.
46  * This approach could save about 40 bytes.
47  * <br>
48  * However the GapBoxView is able to unload its children
49  * dynamically which can save much more memory
50  * than the described simplified implementation.
51  *
52  * @author Miloslav Metelka
53  * @version 1.00
54  */

55
56 class GapBoxViewChildren extends GapObjectArray implements GapObjectArray.RemoveUpdater {
57
58     GapBoxViewChildren(GapBoxView view) {
59         this.view = view;
60
61         indexGapLength = INITIAL_INDEX_GAP_LENGTH;
62         majorAxisOffsetGapLength = INITIAL_MAJOR_AXIS_OFFSET_GAP_LENGTH;
63     }
64
65     /**
66      * Initial size of the index gap.
67      * <br>
68      * The value is chosen big enough so that
69      * no index ever reaches a value
70      * equal to half of it (about 500 million).
71      * That allows to use the gap length
72      * for testing of whether the particular
73      * raw index is below or above the gap.
74      */

75     private static final int INITIAL_INDEX_GAP_LENGTH
76         = Integer.MAX_VALUE >> 1;
77
78     /**
79      * Initial size of the offset gap.
80      *
81      * <p>
82      * Double is chosen for gap length instead of float
83      * because for y as major axis
84      * the truncation could occur when computing the offset
85      * for large files.
86      * <br>
87      * The resolution of mantissa of floats is 23 bits
88      * so assuming the line height is e.g. 17 pixels
89      * and we have more than 250.000 lines in the docuemnt
90      * (which is a lot to write but not so much for e.g.
91      * generated xml files) the last bit would be lost
92      * resulting in every odd line being shifted one
93      * pixel above incorrectly.
94      * <br>
95      * The chosen double is big enough to cover
96      * arbitrarily long documents but retain fractions
97      * when fractional metrics would be used.
98      * <br>
99      * When zero is used as initial gap length
100      * then the gap length will be under zero
101      * but there are no problems with it
102      * as the index of gap start is held.
103      */

104     private static final double INITIAL_MAJOR_AXIS_OFFSET_GAP_LENGTH
105         = 0d;
106
107     /**
108      * A <code>GapBoxView</code> instance for which
109      * this class operates.
110      */

111     protected final GapBoxView view; // 20 bytes GapObjectArray + 4 = 24 bytes
112

113     /**
114      * Length of the offset gap in the children.
115      *
116      * <p>
117      * Double is chosen instead of float because for y as major axis
118      * the truncation could occur when computing the offset
119      * for large files.
120      * <br>
121      * The resolution of mantissa of floats is 23 bits
122      * so assuming the line height is e.g. 17 pixels
123      * and we have more than 250.000 lines in the docuemnt
124      * (which is a lot to write but not so much for e.g.
125      * generated xml files) the last bit would be lost
126      * resulting in every odd line being shifted one
127      * pixel above incorrectly.
128      */

129     private double majorAxisOffsetGapLength; // 24 + 8 = 32 bytes
130

131     /**
132      * Index of the first child that has it <code>majorAxisRawOffset</code>
133      * increased by <code>majorAxisOffsetGapLength</code>.
134      */

135     private int majorAxisOffsetGapIndex; // 32 + 4 = 36 bytes
136

137     /**
138      * Length of the index gap in children.
139      * <br>
140      * The initial gap length is chosen big enough
141      * so that even a half of the gap length value
142      * should never be reached. That allows
143      * to use the gap length for testing
144      * whether the particular raw index
145      * is below or above the gap.
146      */

147     private int indexGapLength; // 36 + 4 = 40 bytes
148

149     /**
150      * Index of the first child which needs the layout update.
151      * <br>
152      * The value is only valid if <code>updateLayoutChildCount &gt= 0</code>.
153      * <br>
154      * Although the layout-related information of the child is invalid
155      * its majorAxisRawOffset should hold the right value.
156      * <br>
157      * The index should always point to a non-flyweight child.
158      */

159     private int firstUpdateLayoutChildIndex; // 40 + 4 = 44 bytes
160

161     /**
162      * Number of children that need layout update.
163      * <br>
164      * The range starts at <code>firstUpdateLayoutChildIndex</code>
165      * and ends at <code>firstUpdateLayoutChildIndex + updateLayoutChildCount</code>
166      */

167     private int updateLayoutChildCount; // 44 + 4 = 48 bytes
168

169     /**
170      * Preferred span of the whole view along the minor axis.
171      */

172     private float minorAxisPreferredSpan; // 48 + 4 = 52 bytes
173

174     /**
175      * Index of the child that has a maximum preferred span along
176      * the minor axis among all the children.
177      * <br>
178      * -1 means there are no children available yet.
179      */

180     private int maxMinorAxisPreferredSpanChildIndex = -1; // 52 + 4 = 56 bytes
181

182     /**
183      * Index of the first child that needs to be repainted
184      * during the next flushing of the requirement changes.
185      */

186     private int firstRepaintChildIndex = -1; // 56 + 4 = 60 bytes
187

188     // Mem: 60 (64 aligned) + 16+ GapObjectArray.array
189

190     public int getChildCount() {
191         return getItemCount();
192     }
193
194     public ViewLayoutState getChild(int index) {
195         return (ViewLayoutState)getItem(index);
196     }
197
198     public int getChildIndex(ViewLayoutState child) {
199         int childIndex = getChildIndexNoCheck(child);
200         if (childIndex >= getChildCount() || getChild(childIndex) != child) {
201             childIndex = -1;
202         }
203         return childIndex;
204     }
205
206     public int getChildIndexNoCheck(ViewLayoutState child) {
207         return getTranslatedChildIndex(child.getViewRawIndex());
208     }
209
210     private int getTranslatedChildIndex(int rawIndex) {
211         if (rawIndex >= indexGapLength) {
212             rawIndex -= indexGapLength;
213         }
214         return rawIndex;
215     }
216
217     public void replace(int index, int length, View JavaDoc[] views) {
218         // The method gets called only if there is something to do
219
// i.e. (length > 0) || (views.length > 0)
220

221         checkConsistency();
222
223         int insertLength = (views != null) ? views.length : 0;
224
225 // System.out.println("getChildCount()=" + getChildCount() + ", index=" + index + ", length=" + length + ", insertLength=" + insertLength);
226

227         // Update indexes that would lay in the removed area
228
// and recompute indexes according to (insertLength - removeLength)
229
if (isReplaceRemovingIndexes(index, length)) { // need to find neighbor
230
/* Find a neighbor of the children being removed.
231              * The neighbor will possibly replace the children
232              * in the internal datastructures.
233              */

234             int neighborIndex;
235             int neighborIndexAfterReplace;
236             ViewLayoutState neighbor;
237             int endRemoveIndex = index + length;
238             if (index > 0) {
239                 neighborIndex = index - 1;
240                 neighborIndexAfterReplace = neighborIndex;
241                 neighbor = getChild(neighborIndex);
242
243             } else if (endRemoveIndex < getChildCount()) {
244                 neighborIndex = endRemoveIndex;
245                 neighborIndexAfterReplace = index + insertLength;
246                 neighbor = getChild(neighborIndex);
247             } else { // no neighbor
248
neighborIndex = -1;
249                 neighborIndexAfterReplace = -1;
250                 neighbor = null;
251             }
252
253             replaceUpdateIndexes(index, length, insertLength,
254                 neighborIndex, neighborIndexAfterReplace, neighbor);
255             
256         } else { // not removing important children to which indexes point
257
replaceUpdateIndexes(index, length, insertLength);
258         }
259
260         double childMajorAxisOffset = 0;
261         if (length == 0) { // no remove so will certainly be adding
262
if (getChildCount() == 0) { // adding to empty
263
ensureCapacity(insertLength); // should alloc exactly the insertLength
264
}
265
266         } else { // at least one child removed
267
int endIndex = index + length;
268             
269             removeInvalidChildIndexesArea(index, length);
270
271             moveMajorAxisOffsetGap(endIndex); // endIndex to leave removed with natural offsets
272
moveIndexGap(endIndex);
273
274             // Update offset gap
275
childMajorAxisOffset = getMajorAxisOffset(index); // under gap because length > 0
276
double majorAxisRemovedSpan = getMajorAxisOffset(endIndex)
277                 - childMajorAxisOffset;
278             majorAxisOffsetGapIndex = index;
279             majorAxisOffsetGapLength += majorAxisRemovedSpan; // increase the gap
280

281             // Update indexes
282
indexGapLength += length;
283
284             // The removeUpdate() gets invoked for each removed ViewLayoutState
285
remove(index, length, this); // remove from gap object array
286

287         }
288
289         // Handle insert
290
if (insertLength > 0) {
291             if (length == 0) { // no removed children
292
moveIndexGap(index);
293                 moveMajorAxisOffsetGap(index);
294                 childMajorAxisOffset = getMajorAxisOffset(index) - majorAxisOffsetGapLength;
295             }
296
297             // childMajorAxisOffset must now be below offset gap
298

299             int majorAxis = view.getMajorAxis();
300             boolean estimatedSpan = view.isEstimatedSpan();
301             boolean childEstimatedSpan;
302             boolean insertLengthAboveThreshold;
303             if (estimatedSpan) {
304                 childEstimatedSpan = true;
305                 insertLengthAboveThreshold = false; // do not schedule resetting
306

307             } else { // not view's estimated span
308
insertLengthAboveThreshold = (insertLength >= view.getReplaceEstimatedThreshold());
309                 childEstimatedSpan = insertLengthAboveThreshold;
310             }
311             
312             for (int i = 0; i < insertLength; i++) {
313                 ViewLayoutState child = view.createChild(views[i]);
314                 View JavaDoc childView = child.getView();
315                 int childIndex = index + i;
316
317                 if (childEstimatedSpan && (childView instanceof EstimatedSpanView)) {
318                     ((EstimatedSpanView)childView).setEstimatedSpan(childEstimatedSpan);
319                 }
320
321                 child.selectLayoutMajorAxis(majorAxis);
322
323                 insertItem(childIndex, child);
324                 indexGapLength--;
325                 majorAxisOffsetGapIndex++;
326
327                 if (!child.isFlyweight()) {
328                     child.setViewRawIndex(childIndex); // all added are below gap
329
child.setLayoutMajorAxisRawOffset(childMajorAxisOffset);
330                     
331                     // Set the parent => that may invoke bunch of actions
332
// including change of the major axis span
333
childView.setParent(view);
334                     
335                     // Make sure the layout gets updated for the child
336
child.viewPreferenceChanged(true, true);
337                     
338                     // Index gap should not be moved because of the constraints for the GapBoxView
339
// (setting of the parent must not cause another view adding/removal)
340
// Offset map could be moved by calling childView.setParent()
341
moveMajorAxisOffsetGap(childIndex + 1);
342                     
343                     // Need to recompute childMajorAxisOffset - could be changed by setParent()
344
// Offset gap above the child -> no need to translate raw offset
345
childMajorAxisOffset = child.getLayoutMajorAxisRawOffset() + child.getLayoutMajorAxisPreferredSpan();
346
347                 } else { // flyweight view
348
childMajorAxisOffset += child.getLayoutMajorAxisPreferredSpan();
349                 }
350                 
351             }
352             
353             if (childEstimatedSpan && !estimatedSpan) {
354                 view.resetEstimatedSpan(index, insertLength);
355             }
356         }
357         
358         // major axis has changed very likely - would be hard to verify precisely so mark for sure
359
view.markMajorAxisPreferenceChanged();
360         // Mark child indexes as invalid - make it as last operation because
361
// it may be decided to update the layout immediately
362
markLayoutInvalid(index, insertLength);
363         // assuming change along major axis => make sure the repaint till the end will follow
364
view.markRepaint(index, true);
365         
366         // No need to schedule update of the whole children.
367
// It's handled in GapBoxView.replace()
368

369         checkConsistency();
370     }
371
372     // Implements GapObjectArray.RemoveUpdater
373
public void removeUpdate(Object JavaDoc removedItem) {
374         releaseChild((ViewLayoutState)removedItem);
375     }
376
377     protected void releaseChild(ViewLayoutState child) {
378         if (!child.isFlyweight()) {
379             child.getView().setParent(null);
380         }
381         
382     }
383
384     /**
385      * Check whether ongoing replace will remove any children
386      * to which important indexes point.
387      *
388      * @return true if the replace will remove the important children
389      * or false otherwise.
390      */

391     protected boolean isReplaceRemovingIndexes(int index, int removeLength) {
392         int ind = getMaxMinorAxisPreferredSpanChildIndex();
393         return (!view.isChildrenLayoutNecessary()
394             && ind >= index && ind < index + removeLength);
395     }
396             
397
398     /**
399      * Update indices because of the replace.
400      * <br>
401      * None of the important indexes points to any of the removed children.
402      *
403      * @param index index at which the replace is done
404      * @param removeLength number of removed children
405      * @param insertLength number of inserted children
406      * @return true if the minor axis was likely changed due to replace done
407      * in children or false otherwise.
408      */

409     protected void replaceUpdateIndexes(int index, int removeLength, int insertLength) {
410         // Work only if the complete children layout is not scheduled.
411
// If it is then the indexes could be invalid anyway.
412
if (!view.isChildrenLayoutNecessary()) {
413             int endRemoveIndex = index + removeLength;
414             // Check whether the index is not above the removed area and if so
415
// update it. It cannot be inside removed area otherwise
416
// update with neighbor would be invoked instead of this method.
417
int ind = getMaxMinorAxisPreferredSpanChildIndex();
418             if (ind >= endRemoveIndex) { // must update by diff
419
setMaxMinorAxisPreferredSpanChildIndex(ind + insertLength - removeLength);
420             }
421         }
422     }
423     
424     /**
425      * Update indices because of the replace.
426      * @param index index at which the replace is done
427      * @param removeLength number of removed children
428      * @param insertLength number of inserted children
429      * @param neighborIndex index of the neighbor that certainly stays in the array
430      * after the possible removal takes place. If no children stay then
431      * the index is -1. The index is given in original index space
432      * i.e. if the neighbor is above removed area then
433      * <code>insertLength - removeLength</code> must be added to it
434      * so that index after removal is obtained.
435      * @param neighborIndexAfterReplace index of the neighbor that stays
436      * in the array updated so that it points to the "real" after-replace index.
437      * It's -1 if neighbor index is -1.
438      * @param neighbor neighbor child that stays in the array or null
439      * if there is no such child (index is -1).
440      * @return true if the minor axis was likely changed due to replace done
441      * in children or false otherwise.
442      */

443     protected void replaceUpdateIndexes(int index, int removeLength, int insertLength,
444     int neighborIndex, int neighborIndexAfterReplace, ViewLayoutState neighbor) {
445         
446         // Work only if the complete children layout is not scheduled.
447
// If it is then the indexes could be invalid anyway.
448
if (!view.isChildrenLayoutNecessary()) {
449             int endRemoveIndex = index + removeLength;
450             /* Check whether any of the removed children points
451              * to maxMinorAxisPreferredSpanChildIndex.
452              * If so update the index to point to the neighbor if appropriate.
453              *
454              * BTW here as there is just one tested index
455              * it is clear that the index points into the affected area
456              * so in fact there would not have to be checking for that.
457              * However if there would several different indexes
458              * then e.g. just one could be affected.
459              */

460             int ind = getMaxMinorAxisPreferredSpanChildIndex();
461             if (ind >= endRemoveIndex) { // must update by diff
462
setMaxMinorAxisPreferredSpanChildIndex(ind + insertLength - removeLength);
463
464             } else if (ind >= index) { // in removed area -> need to change to neighbor
465
if (neighbor == null || neighbor.getLayoutMinorAxisPreferredSpan()
466                     < view.getMinorAxisPreferredSpan()
467                 ) {
468                     // Do not know whether there is not any other child
469
// with the same span as the removed child.
470
// Anyway need the complete layout to check that.
471
view.markChildrenLayoutNecessary();
472                 }
473                 setMaxMinorAxisPreferredSpanChildIndex(neighborIndexAfterReplace);
474             }
475         }
476     }
477
478     /**
479      * Get the visual offset of the child along the major axis.
480      */

481     public double getMajorAxisOffset(int childIndex) {
482         if (childIndex < getChildCount()) { // to be able to return offset after last view
483
ViewLayoutState child = getChild(childIndex);
484             if (!child.isFlyweight()) {
485                 double offset = child.getLayoutMajorAxisRawOffset();
486                 if (childIndex >= majorAxisOffsetGapIndex) {
487                     offset -= majorAxisOffsetGapLength;
488                 }
489                 return offset;
490             }
491         }
492
493         double majorAxisOffset = 0;
494         while (--childIndex >= 0) {
495             ViewLayoutState child = getChild(childIndex);
496             majorAxisOffset += child.getLayoutMajorAxisPreferredSpan();
497             if (!child.isFlyweight()) {
498                 double offset = child.getLayoutMajorAxisRawOffset();
499                 if (childIndex >= majorAxisOffsetGapIndex) {
500                     offset -= majorAxisOffsetGapLength;
501                 }
502                 majorAxisOffset += offset;
503                 break;
504             }
505         }
506
507         return majorAxisOffset;
508     }
509
510     /**
511      * Get current value of the preferred span along major axis
512      * by subtracting offset gap length from the initial gap length
513      */

514     protected double getMajorAxisPreferredSpan() {
515         return INITIAL_MAJOR_AXIS_OFFSET_GAP_LENGTH - majorAxisOffsetGapLength;
516     }
517     
518     protected final float getMinorAxisPreferredSpan() {
519         return minorAxisPreferredSpan;
520     }
521     
522     protected void setMinorAxisPreferredSpan(float minorAxisPreferredSpan) {
523         this.minorAxisPreferredSpan = minorAxisPreferredSpan;
524     }
525
526     /**
527      * What is the offset along the minor axis
528      */

529     protected float getMinorAxisOffset(ViewLayoutState child) {
530         float minorAxisAssignedSpan = view.getMinorAxisAssignedSpan();
531         float childPreferredSpan = child.getLayoutMinorAxisPreferredSpan();
532         if (childPreferredSpan < minorAxisAssignedSpan) {
533             // can't make the child to fill the whole span, so align it
534
float align = child.getLayoutMinorAxisAlignment();
535             return ((minorAxisAssignedSpan - childPreferredSpan) * align);
536         }
537
538         return 0f;
539     }
540     
541     protected float getMinorAxisSpan(ViewLayoutState child) {
542         float minorAxisAssignedSpan = view.getMinorAxisAssignedSpan();
543         float childPreferredSpan = child.getLayoutMinorAxisPreferredSpan();
544         return (childPreferredSpan < minorAxisAssignedSpan) // child span lower than overall span
545
? childPreferredSpan
546             : minorAxisAssignedSpan;
547     }
548
549     /**
550      * Notification that a particular child has changed its preference
551      * along the major axis.
552      * <br>
553      * No layout update is scheduled as it's assumed
554      * that the child has already caused a layout update being scheduled
555      * by calling <code>View.preferenceChanged()</code> in its parent.
556      */

557     protected void majorAxisPreferenceChanged(ViewLayoutState child,
558     int childIndex, double majorAxisSpanDelta) {
559
560         moveMajorAxisOffsetGap(childIndex + 1);
561         majorAxisOffsetGapLength -= majorAxisSpanDelta;
562         view.markMajorAxisPreferenceChanged();
563     }
564
565     /**
566      * Notification that a particular child has changed its preference
567      * along the minor axis.
568      * <br>
569      * No layout update is scheduled as it's assumed
570      * that the child has already caused a layout update being scheduled
571      * by calling <code>View.preferenceChanged()</code> in its parent.
572      */

573     protected void minorAxisPreferenceChanged(ViewLayoutState child, int childIndex) {
574         // Work only if the complete children layout is not scheduled.
575
// If it is then the indexes could be invalid anyway.
576
if (!view.isChildrenLayoutNecessary()) {
577             float preferredSpan = child.getLayoutMinorAxisPreferredSpan();
578             float minorAxisPreferredSpan = getMinorAxisPreferredSpan();
579             int maxPreferredSpanIndex= getMaxMinorAxisPreferredSpanChildIndex();
580
581             if (maxPreferredSpanIndex == -1 // no children yet
582
|| preferredSpan > minorAxisPreferredSpan
583             ) {
584                 setMinorAxisPreferredSpan(preferredSpan);
585                 view.markMinorAxisPreferenceChanged(); // span is wider -> chnage preference
586
setMaxMinorAxisPreferredSpanChildIndex(childIndex);
587
588             } else if (childIndex == maxPreferredSpanIndex
589                 && preferredSpan < minorAxisPreferredSpan
590             ) { // used to be max but now shrinked - which should now be max?
591
// Do not know here - need complete layout to decide
592
// which is max and whether total span has shrinked
593
view.markChildrenLayoutNecessary();
594             }
595         }
596     }
597     
598     public int getChildStartOffset(int childIndex) {
599         ViewLayoutState child = getChild(childIndex);
600         if (!child.isFlyweight()) {
601             return child.getView().getStartOffset();
602         }
603         
604         int startOffset = 0;
605         while (--childIndex >= 0) {
606             child = getChild(childIndex);
607             /* In case of flyweight view the length of the view is added.
608              * In case of regular view it's ending offset.
609              */

610             startOffset += child.getView().getEndOffset();
611             if (!child.isFlyweight()) {
612                 break;
613             }
614         }
615         
616         return startOffset;
617     }
618
619     public int getChildEndOffset(int childIndex) {
620         int endOffset = 0;
621         while (childIndex >= 0) {
622             ViewLayoutState child = getChild(childIndex--);
623             endOffset += child.getView().getEndOffset();
624             if (!child.isFlyweight()) { // break on first non-flyweight
625
break;
626             }
627         }
628         
629         return endOffset;
630     }
631
632     /**
633      * Get current allocation of a child view in this view.
634      * <em>This does not do update the offsets in the ViewLayoutState
635      * records.</em>
636      * @param index childIndex of the child
637      * @param targetRect rectangle to which the allocation is set.
638      * If it's null a new rectangle is created, set and returned.
639      * @return rectangle filled with the child allocation.
640      */

641     public Rectangle JavaDoc getChildCoreAllocation(int childIndex, Rectangle JavaDoc targetRect) {
642         childrenUpdateLayout(childIndex); // update child layouts till this index
643

644         if (targetRect == null) {
645             targetRect = new Rectangle JavaDoc();
646         }
647
648         ViewLayoutState child = getChild(childIndex);
649         int majorAxisOffsetInt = (int)getMajorAxisOffset(childIndex);
650         int minorAxisOffsetInt = (int)getMinorAxisOffset(child);
651         int majorAxisSpanInt = (int)child.getLayoutMajorAxisPreferredSpan();
652         int minorAxisSpanInt = (int)getMinorAxisSpan(child);
653
654         if (view.isXMajorAxis()) {
655             targetRect.x = majorAxisOffsetInt;
656             targetRect.y = minorAxisOffsetInt;
657             targetRect.width = majorAxisSpanInt;
658             targetRect.height = minorAxisSpanInt;
659
660         } else {
661             targetRect.x = minorAxisOffsetInt;
662             targetRect.y = majorAxisOffsetInt;
663             targetRect.width = minorAxisSpanInt;
664             targetRect.height = majorAxisSpanInt;
665         }
666
667         ViewInsets insets = view.getInsets();
668         if (insets != null) {
669             targetRect.x += (int)insets.getLeft();
670             targetRect.y += (int)insets.getTop();
671         }
672
673         return targetRect;
674     }
675     
676     /**
677      * Locate the view responsible for an offset into the box
678      * along the major axis. Make sure that offsets are set
679      * on the ViewLayoutState objects up to the given target span
680      * past the desired offset.
681      *
682      * @return index of the view representing the given visual
683      * location (majorAxisOffset), or -1 if the offset is below
684      * all the available views. <code>getChildCount()</code> is returned
685      * if the given offset is greater or equal to the visual end
686      * of the last child view.
687      */

688     public int getChildIndexAtCorePoint(float x, float y) {
689         int childCount = getChildCount();
690         int low = 0;
691         int high = childCount - 1;
692         
693         if (high == -1) { // return -1 if there are no children
694
return -1;
695         }
696         
697         double majorAxisOffset = view.isXMajorAxis() ? x : y;
698
699         int luChildCount = getUpdateLayoutChildCount();
700         if (luChildCount > 0) { // some children not validated yet
701
int firstLUChildIndex = getFirstUpdateLayoutChildIndex();
702             if (getMajorAxisOffset(firstLUChildIndex) <= majorAxisOffset) { // above invalid
703
childrenUpdateLayout(high); // update till the last child
704
low = firstLUChildIndex;
705                 
706             } else { // below invalid area
707
high = firstLUChildIndex - 1;
708             }
709         }
710         
711         while (low <= high) {
712             int mid = (low + high) / 2;
713             ViewLayoutState child = getChild(mid);
714             double midMajorAxisOffset = getMajorAxisOffset(mid);
715             
716             if (midMajorAxisOffset < majorAxisOffset) {
717                 low = mid + 1;
718             } else if (midMajorAxisOffset > majorAxisOffset) {
719                 high = mid - 1;
720             } else {
721                 // view starting exactly at the given position found
722
return mid;
723             }
724         }
725         
726         // Not found exactly. 'high' points to token that possibly "contains"
727
// the requested offset.
728

729         // Return childCount if the last view does not contain the given offset
730
if (high < 0) {
731             high = 0;
732         }
733             
734         return high;
735     }
736
737     /**
738      * Paint the children that intersect the clip area.
739      *
740      * @param g graphics to render into.
741      * @param alloc allocation for the parent view including the insets.
742      * The rectangle can be mutated.
743      */

744     protected void paintChildren(Graphics JavaDoc g, Rectangle JavaDoc alloc) {
745         int viewAllocX = alloc.x;
746         int viewAllocY = alloc.y;
747         // int viewAllocWidth = alloc.width;
748
// int viewAllocHeight = alloc.height;
749

750         alloc = g.getClipBounds(alloc); // get the clip - reuse alloc variable
751
if (alloc == null) { // no clip => return
752
return;
753         }
754         int clipX = alloc.x;
755         int clipY = alloc.y;
756         boolean isXMajorAxis = view.isXMajorAxis();
757         int clipEnd = isXMajorAxis
758             ? (clipX + alloc.width)
759             : (clipY + alloc.height);
760         
761
762         int childIndex = getChildIndexAtCorePoint(clipX, clipY);
763         int childCount = getChildCount();
764
765         for (int i = Math.max(childIndex, 0); i < childCount; i++) {
766             ViewLayoutState child = getChild(i);
767             // Reuse alloc for child's allocation
768
alloc = getChildCoreAllocation(i, alloc);
769             alloc.x += viewAllocX;
770             alloc.y += viewAllocY;
771             /*
772              * Paint the child if it lies before the end of the clip.
773              * We should also check whether the child
774              * fits into the allocation given to this view by checking whether
775              * parentViewAllocation.intersects(childAllocation))
776              * but for the orthogonal use this should not be necessary.
777              */

778             int allocStart = isXMajorAxis ? alloc.x : alloc.y;
779                     
780             if (allocStart < clipEnd) { // child at least partly inside clip
781

782                 View JavaDoc v = child.getView();
783                 v.paint(g, alloc);
784             } else {
785                 break; // stop painting of children
786
}
787         }
788     }
789
790     protected final int getFirstRepaintChildIndex() {
791         return firstRepaintChildIndex;
792     }
793     
794     final void setFirstRepaintChildIndex(int firstRepaintChildIndex) {
795         this.firstRepaintChildIndex = firstRepaintChildIndex;
796     }
797
798     final int getMaxMinorAxisPreferredSpanChildIndex() {
799         return maxMinorAxisPreferredSpanChildIndex;
800     }
801
802     void setMaxMinorAxisPreferredSpanChildIndex(int maxMinorAxisPreferredSpanChildIndex) {
803         this.maxMinorAxisPreferredSpanChildIndex = maxMinorAxisPreferredSpanChildIndex;
804     }
805
806     /**
807      * Get index of the first child that needs layout update.
808      */

809     final int getFirstUpdateLayoutChildIndex() {
810         return firstUpdateLayoutChildIndex;
811     }
812     
813     /**
814      * Get number of children that need layout update.
815      * <br>
816      * The first child that needs layout is determined
817      * by {@link #getFirstUpdateLayoutChildIndex()}.
818      */

819     final int getUpdateLayoutChildCount() {
820         return updateLayoutChildCount;
821     }
822     
823     void markLayoutInvalid(int firstChildIndex, int count) {
824         if (count > 0) {
825             if (updateLayoutChildCount > 0) {
826                 int endInvalid = firstUpdateLayoutChildIndex + updateLayoutChildCount; // precede assignment that follows
827
firstUpdateLayoutChildIndex = Math.min(firstUpdateLayoutChildIndex, firstChildIndex);
828                 updateLayoutChildCount = Math.max(endInvalid, firstChildIndex + count)
829                     - firstUpdateLayoutChildIndex;
830
831             } else { // no invalid indexes yet
832
firstUpdateLayoutChildIndex = firstChildIndex;
833                 updateLayoutChildCount = count;
834                 // Mark the layout of the view as invalid (propagate to parent).
835
// This must be done as last operation here because
836
// parent may immediately start updating of the children' layout
837
view.markLayoutInvalid();
838             }
839         }
840     }
841
842     /**
843      * Forget that indexes in the given index area were possibly
844      * marked as invalid (needing layout recomputation).
845      */

846     void removeInvalidChildIndexesArea(int firstChildIndex, int count) {
847         int endChildIndex = firstChildIndex + count;
848         if (updateLayoutChildCount > 0) { // some invalid children
849
int endInvalid = firstUpdateLayoutChildIndex + updateLayoutChildCount;
850             if (endInvalid > firstChildIndex) { // overlap or after
851
if (firstUpdateLayoutChildIndex >= endChildIndex) { // after removed range
852
firstUpdateLayoutChildIndex -= count;
853                 } else if (firstUpdateLayoutChildIndex < firstChildIndex) { // starts below invalid area
854
updateLayoutChildCount -= Math.min(endInvalid, endChildIndex) - firstChildIndex;
855                 } else { // invalid are starts inside removed area
856
updateLayoutChildCount -= Math.min(endInvalid, endChildIndex) - firstUpdateLayoutChildIndex;
857                     firstUpdateLayoutChildIndex = endChildIndex;
858                     while (updateLayoutChildCount > 0 && getChild(firstUpdateLayoutChildIndex).isFlyweight()) {
859                         firstUpdateLayoutChildIndex++;
860                         updateLayoutChildCount--;
861                     }
862                 }
863             }
864         }
865     }
866
867     /**
868      * If necessary update layout of the particular child and possibly
869      * all the children that precede it that require their layout to be updated.
870      *
871      * @param tillChildIndex index of the child that should be updated. Indexes above it
872      * can stay not updated.
873      */

874     protected final void childrenUpdateLayout(int tillChildIndex) {
875         while (updateLayoutChildCount > 0 && firstUpdateLayoutChildIndex <= tillChildIndex) {
876             updateLayoutChildCount--;
877             ViewLayoutState child = getChild(firstUpdateLayoutChildIndex++);
878             child.updateLayout();
879         }
880     }
881     
882     protected final void childrenUpdateLayout() {
883         childrenUpdateLayout(Integer.MAX_VALUE);
884     }
885     
886     /**
887      * Compute complete layout information of the children along the minor axis.
888      * This is typically done when a particular child changes its layout
889      * in a way that affects the whole view. For example if the child
890      * having the maximum span along the minor axis shrinks then
891      * it's unknown whether it's still the one with the maximum span
892      * or whether there isn't other child having the same or bigger span.
893      * <br>
894      * This default implementation only computes a child with maximum
895      * preferred span along the minor axis that defines the span
896      * of the whole view but subclasses may do e.g. baseline layout.
897      * <br>
898      * This method is called during layout update of the children.
899      */

900     protected void childrenLayout() {
901         // Find child with maximum prefered span
902
// along the minor axis
903

904         int childCount = getChildCount();
905         int maxPreferredSpanChildIndex = -1;
906         float maxPreferredSpan = 0f;
907
908         for (int i = 0; i < childCount; i++) {
909             ViewLayoutState child = getChild(i);
910             float span = child.getLayoutMinorAxisPreferredSpan();
911             if (span > maxPreferredSpan) {
912                 maxPreferredSpanChildIndex = i;
913                 maxPreferredSpan = span;
914             }
915
916         }
917         // Remember the maximum child
918
setMaxMinorAxisPreferredSpanChildIndex(maxPreferredSpanChildIndex);
919
920         // This will be preferred span of the view along minor axis
921
if (maxPreferredSpan != getMinorAxisPreferredSpan()) {
922             setMinorAxisPreferredSpan(maxPreferredSpan);
923             view.markMinorAxisPreferenceChanged();
924         }
925     }
926
927     protected void unload() {
928         int childCount = getChildCount();
929         for (int i = 0; i < childCount; i++) {
930             releaseChild(getChild(i));
931         }
932     }
933
934     private void moveIndexGap(int index) {
935         checkConsistency();
936
937         int gapLen = indexGapLength; // cache to local var
938
int belowIndex = index;
939         boolean updated = false;
940         while (--belowIndex >= 0) {
941             ViewLayoutState child = getChild(belowIndex);
942             if (!child.isFlyweight()) {
943                 int rawIndex = child.getViewRawIndex();
944                 if (rawIndex >= gapLen) {
945                     child.setViewRawIndex(rawIndex - gapLen);
946                     updated = true;
947                 } else { // below index gap
948
break; // stop
949
}
950             }
951         }
952
953         if (!updated) { // need to check whether the gap starts at index
954
int childCount = getChildCount();
955             while (index < childCount) {
956                 ViewLayoutState child = getChild(index++);
957                 if (!child.isFlyweight()) {
958                     int rawIndex = child.getViewRawIndex();
959                     if (rawIndex < gapLen) { // below gap
960
child.setViewRawIndex(rawIndex + gapLen);
961                     } else {
962                         break; // found first child already above gap
963
}
964                 }
965             }
966         }
967
968         checkConsistency();
969     }
970
971     private void moveMajorAxisOffsetGap(int index) {
972         if (index == majorAxisOffsetGapIndex) {
973             return; // no work
974
}
975
976         checkConsistency();
977
978         if (index < majorAxisOffsetGapIndex) { // need to move gap start down
979
while (--majorAxisOffsetGapIndex >= index) {
980                 ViewLayoutState child = getChild(majorAxisOffsetGapIndex);
981                 if (!child.isFlyweight()) {
982                     child.setLayoutMajorAxisRawOffset(child.getLayoutMajorAxisRawOffset()
983                         + majorAxisOffsetGapLength);
984                 }
985             }
986             majorAxisOffsetGapIndex++;
987
988         } else { // index above gap index or equal to it
989
while (majorAxisOffsetGapIndex < index) { // need to move gap start down
990
ViewLayoutState child = getChild(majorAxisOffsetGapIndex);
991                 if (!child.isFlyweight()) {
992                     child.setLayoutMajorAxisRawOffset(child.getLayoutMajorAxisRawOffset()
993                         - majorAxisOffsetGapLength);
994                 }
995                 majorAxisOffsetGapIndex++;
996             }
997         }
998
999         checkConsistency();
1000    }
1001
1002    private void checkConsistency() {
1003        if (true) {
1004            return; // disabled
1005
}
1006// System.out.println(toStringDetail());
1007

1008        int childCount = getChildCount();
1009        if (majorAxisOffsetGapIndex > childCount) {
1010            throw new IllegalStateException JavaDoc(
1011                "majorAxisOffsetGapIndex=" + majorAxisOffsetGapIndex // NOI18N
1012
+ " > childCount=" + childCount // NOI18N
1013
);
1014        }
1015
1016        int indexGapStart = computeIndexGapStart();
1017        int lastRawIndex = -1;
1018        double lastMajorAxisOffset = 0;
1019        for (int i = 0; i < childCount; i++) {
1020            ViewLayoutState child = getChild(i);
1021
1022            int rawIndex = child.getViewRawIndex();
1023            if (rawIndex <= lastRawIndex) {
1024                throw new IllegalStateException JavaDoc("rawIndex=" + rawIndex // NOI18N
1025
+ " at index=" + i // NOI18N
1026
+ " <= lastRawIndex=" + lastRawIndex); // NOI18N
1027
}
1028
1029            if (rawIndex >= indexGapLength && rawIndex < indexGapStart) {
1030                throw new IllegalStateException JavaDoc("Above gap rawIndex=" + rawIndex // NOI18N
1031
+ " at index=" + i // NOI18N
1032
+ " but gap seems to start at index " + indexGapStart); // NOI18N
1033
}
1034
1035            if (rawIndex < indexGapLength && rawIndex >= indexGapStart) {
1036                throw new IllegalStateException JavaDoc("Below gap rawIndex=" + rawIndex // NOI18N
1037
+ " at index=" + i // NOI18N
1038
+ " but gap seems to start at index " + indexGapStart); // NOI18N
1039
}
1040
1041            if (getChildIndexNoCheck(child) != i) {
1042                throw new IllegalStateException JavaDoc("Child at index=" + i // NOI18N
1043
+ " has internal index=" + getChildIndexNoCheck(child)); // NOI18N
1044
}
1045
1046
1047            double childMajorAxisOffset = child.getLayoutMajorAxisRawOffset();
1048            if (i >= majorAxisOffsetGapIndex) {
1049                childMajorAxisOffset -= majorAxisOffsetGapLength;
1050            }
1051
1052            if (childMajorAxisOffset < lastMajorAxisOffset) {
1053                throw new IllegalStateException JavaDoc(
1054                    "childMajorAxisOffset=" + childMajorAxisOffset // NOI18N
1055
+ " at index=" + i // NOI18N
1056
+ " < lastMajorAxisOffset=" + lastMajorAxisOffset); // NOI18N
1057
}
1058
1059            if (childMajorAxisOffset < 0) {
1060                throw new IllegalStateException JavaDoc("Major axis offset at index=" + i // NOI18N
1061
+ " is " + childMajorAxisOffset + " < 0"); // NOI18N
1062
}
1063        }
1064    }
1065
1066    private int computeIndexGapStart() {
1067        int childCount = getChildCount();
1068        for (int i = 0; i < childCount; i++) {
1069            if (getChild(i).getViewRawIndex() >= indexGapLength) {
1070                return i;
1071            }
1072        }
1073        return childCount;
1074    }
1075
1076    public String JavaDoc toStringDetail() {
1077        StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1078        sb.append(view.toString());
1079        sb.append(", indexGapStart="); // NOI18N
1080
sb.append(computeIndexGapStart());
1081        sb.append(", majorAxisOffsetGapIndex="); // NOI18N
1082
sb.append(majorAxisOffsetGapIndex);
1083        sb.append(", majorAxisOffsetGapLength="); // NOI18N
1084
sb.append(majorAxisOffsetGapLength);
1085        
1086
1087        sb.append(view.childrenToString());
1088
1089        return sb.toString();
1090    }
1091
1092}
1093
Popular Tags