1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49:
50:
79: public class AsyncBoxView
80: extends View
81: {
82:
83:
88: public class ChildLocator
89: {
90:
91:
94: protected ChildState lastValidOffset;
95:
96:
99: protected Rectangle lastAlloc;
100:
101:
105: protected Rectangle childAlloc;
106:
107:
110: public ChildLocator()
111: {
112: lastAlloc = new Rectangle();
113: childAlloc = new Rectangle();
114: }
115:
116:
127: public synchronized void childChanged(ChildState cs)
128: {
129: if (lastValidOffset == null
130: || cs.getChildView().getStartOffset()
131: < lastValidOffset.getChildView().getStartOffset())
132: {
133: lastValidOffset = cs;
134: }
135: }
136:
137:
148: public int getViewIndexAtPoint(float x, float y, Shape a)
149: {
150: setAllocation(a);
151: float targetOffset = (getMajorAxis() == X_AXIS) ? x - lastAlloc.x
152: : y - lastAlloc.y;
153: int index = getViewIndexAtVisualOffset(targetOffset);
154: return index;
155: }
156:
157:
166: public synchronized Shape getChildAllocation(int index, Shape a)
167: {
168: if (a == null)
169: return null;
170: setAllocation(a);
171: ChildState cs = getChildState(index);
172: if (cs.getChildView().getStartOffset()
173: > lastValidOffset.getChildView().getStartOffset())
174: {
175: updateChildOffsetsToIndex(index);
176: }
177: Shape ca = getChildAllocation(index);
178: return ca;
179: }
180:
181:
186: public synchronized void paintChildren(Graphics g)
187: {
188: Rectangle clip = g.getClipBounds();
189: float targetOffset = (getMajorAxis() == X_AXIS) ? clip.x - lastAlloc.x
190: : clip.y - lastAlloc.y;
191: int index = getViewIndexAtVisualOffset(targetOffset);
192: int n = getViewCount();
193: float offs = getChildState(index).getMajorOffset();
194: for (int i = index; i < n; i++)
195: {
196: ChildState cs = getChildState(i);
197: cs.setMajorOffset(offs);
198: Shape ca = getChildAllocation(i);
199: if (ca.intersects(clip))
200: {
201: synchronized (cs)
202: {
203: View v = cs.getChildView();
204: v.paint(g, ca);
205: }
206: }
207: else
208: {
209:
210: break;
211: }
212: offs += cs.getMajorSpan();
213: }
214: }
215:
216:
225: protected Shape getChildAllocation(int index)
226: {
227: ChildState cs = getChildState(index);
228: if (! cs.isLayoutValid())
229: cs.run();
230:
231: if (getMajorAxis() == X_AXIS)
232: {
233: childAlloc.x = lastAlloc.x + (int) cs.getMajorOffset();
234: childAlloc.y = lastAlloc.y + (int) cs.getMinorOffset();
235: childAlloc.width = (int) cs.getMajorSpan();
236: childAlloc.height = (int) cs.getMinorSpan();
237: }
238: else
239: {
240: childAlloc.y = lastAlloc.y + (int) cs.getMajorOffset();
241: childAlloc.x = lastAlloc.x + (int) cs.getMinorOffset();
242: childAlloc.height = (int) cs.getMajorSpan();
243: childAlloc.width = (int) cs.getMinorSpan();
244: }
245: return childAlloc;
246: }
247:
248:
253: protected void setAllocation(Shape a)
254: {
255: if (a instanceof Rectangle)
256: lastAlloc.setBounds((Rectangle) a);
257: else
258: lastAlloc.setBounds(a.getBounds());
259:
260: setSize(lastAlloc.width, lastAlloc.height);
261: }
262:
263:
272: protected int getViewIndexAtVisualOffset(float targetOffset)
273: {
274: int n = getViewCount();
275: if (n > 0)
276: {
277: if (lastValidOffset == null)
278: lastValidOffset = getChildState(0);
279: if (targetOffset > majorSpan)
280: return 0;
281: else if (targetOffset > lastValidOffset.getMajorOffset())
282: return updateChildOffsets(targetOffset);
283: else
284: {
285: float offs = 0f;
286: for (int i = 0; i < n; i++)
287: {
288: ChildState cs = getChildState(i);
289: float nextOffs = offs + cs.getMajorSpan();
290: if (targetOffset < nextOffs)
291: return i;
292: offs = nextOffs;
293: }
294: }
295: }
296: return n - 1;
297: }
298:
299:
307: private int updateChildOffsets(float targetOffset)
308: {
309: int n = getViewCount();
310: int targetIndex = n - 1;
311: int pos = lastValidOffset.getChildView().getStartOffset();
312: int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
313: float start = lastValidOffset.getMajorOffset();
314: float lastOffset = start;
315: for (int i = startIndex; i < n; i++)
316: {
317: ChildState cs = getChildState(i);
318: cs.setMajorOffset(lastOffset);
319: lastOffset += cs.getMajorSpan();
320: if (targetOffset < lastOffset)
321: {
322: targetIndex = i;
323: lastValidOffset = cs;
324: break;
325: }
326: }
327: return targetIndex;
328: }
329:
330:
335: private void updateChildOffsetsToIndex(int index)
336: {
337: int pos = lastValidOffset.getChildView().getStartOffset();
338: int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
339: float lastOffset = lastValidOffset.getMajorOffset();
340: for (int i = startIndex; i <= index; i++)
341: {
342: ChildState cs = getChildState(i);
343: cs.setMajorOffset(lastOffset);
344: lastOffset += cs.getMajorSpan();
345: }
346: }
347: }
348:
349:
352: public class ChildState
353: implements Runnable
354: {
355:
356:
359: private View childView;
360:
361:
365: private boolean minorValid;
366:
367:
371: private boolean majorValid;
372:
373:
377: boolean childSizeValid;
378:
379:
383: float minimum;
384:
385:
389: float preferred;
390:
391:
394: private float majorSpan;
395:
396:
399: private float majorOffset;
400:
401:
404: private float minorSpan;
405:
406:
409: private float minorOffset;
410:
411:
414: private float maximum;
415:
416:
422: public ChildState(View view)
423: {
424: childView = view;
425: }
426:
427:
433: public View getChildView()
434: {
435: return childView;
436: }
437:
438:
445: public boolean isLayoutValid()
446: {
447: return minorValid && majorValid && childSizeValid;
448: }
449:
450:
454: public void run()
455: {
456: Document doc = getDocument();
457: if (doc instanceof AbstractDocument)
458: {
459: AbstractDocument abstractDoc = (AbstractDocument) doc;
460: abstractDoc.readLock();
461: }
462:
463: try
464: {
465:
466: if (!(minorValid && majorValid && childSizeValid)
467: && childView.getParent() == AsyncBoxView.this)
468: {
469: synchronized(AsyncBoxView.this)
470: {
471: changing = this;
472: }
473: update();
474: synchronized(AsyncBoxView.this)
475: {
476: changing = null;
477: }
478:
479:
480: update();
481: }
482: }
483: finally
484: {
485: if (doc instanceof AbstractDocument)
486: {
487: AbstractDocument abstractDoc = (AbstractDocument) doc;
488: abstractDoc.readUnlock();
489: }
490: }
491: }
492:
493:
497: private void update()
498: {
499: int majorAxis = getMajorAxis();
500: boolean minorUpdated = false;
501: synchronized (this)
502: {
503: if (! minorValid)
504: {
505: int minorAxis = getMinorAxis();
506: minimum = childView.getMinimumSpan(minorAxis);
507: preferred = childView.getPreferredSpan(minorAxis);
508: maximum = childView.getMaximumSpan(minorAxis);
509: minorValid = true;
510: minorUpdated = true;
511: }
512: }
513: if (minorUpdated)
514: minorRequirementChange(this);
515:
516: boolean majorUpdated = false;
517: float delta = 0.0F;
518: synchronized (this)
519: {
520: if (! majorValid)
521: {
522: float oldSpan = majorSpan;
523: majorSpan = childView.getPreferredSpan(majorAxis);
524: delta = majorSpan - oldSpan;
525: majorValid = true;
526: majorUpdated = true;
527: }
528: }
529: if (majorUpdated)
530: {
531: majorRequirementChange(this, delta);
532: locator.childChanged(this);
533: }
534:
535: synchronized (this)
536: {
537: if (! childSizeValid)
538: {
539: float w;
540: float h;
541: if (majorAxis == X_AXIS)
542: {
543: w = majorSpan;
544: h = getMinorSpan();
545: }
546: else
547: {
548: w = getMinorSpan();
549: h = majorSpan;
550: }
551: childSizeValid = true;
552: childView.setSize(w, h);
553: }
554: }
555: }
556:
557:
562: public float getMinorSpan()
563: {
564: float retVal;
565: if (maximum < minorSpan)
566: retVal = maximum;
567: else
568: retVal = Math.max(minimum, minorSpan);
569: return retVal;
570: }
571:
572:
577: public float getMinorOffset()
578: {
579: float retVal;
580: if (maximum < minorSpan)
581: {
582: float align = childView.getAlignment(getMinorAxis());
583: retVal = ((minorSpan - maximum) * align);
584: }
585: else
586: retVal = 0f;
587:
588: return retVal;
589: }
590:
591:
596:
597: public float getMajorSpan()
598: {
599: return majorSpan;
600: }
601:
602:
607: public float getMajorOffset()
608: {
609: return majorOffset;
610: }
611:
612:
618: public void setMajorOffset(float offset)
619: {
620: majorOffset = offset;
621: }
622:
623:
630: public void preferenceChanged(boolean width, boolean height)
631: {
632: if (getMajorAxis() == X_AXIS)
633: {
634: if (width)
635: majorValid = false;
636: if (height)
637: minorValid = false;
638: }
639: else
640: {
641: if (width)
642: minorValid = false;
643: if (height)
644: majorValid = false;
645: }
646: childSizeValid = false;
647: }
648: }
649:
650:
653: private class FlushTask implements Runnable
654: {
655:
661: public void run()
662: {
663: try
664: {
665:
666: Document doc = getDocument();
667: if (doc instanceof AbstractDocument)
668: {
669: AbstractDocument abstractDoc = (AbstractDocument) doc;
670: abstractDoc.readLock();
671: }
672:
673: int n = getViewCount();
674: if (minorChanged && (n > 0))
675: {
676: LayoutQueue q = getLayoutQueue();
677: ChildState min = getChildState(0);
678: ChildState pref = getChildState(0);
679: for (int i = 1; i < n; i++)
680: {
681: ChildState cs = getChildState(i);
682: if (cs.minimum > min.minimum)
683: min = cs;
684: if (cs.preferred > pref.preferred)
685: pref = cs;
686: }
687: synchronized (AsyncBoxView.this)
688: {
689: minReq = min;
690: prefReq = pref;
691: }
692: }
693:
694: flushRequirementChanges();
695: }
696: finally
697: {
698:
699: Document doc = getDocument();
700: if (doc instanceof AbstractDocument)
701: {
702: AbstractDocument abstractDoc = (AbstractDocument) doc;
703: abstractDoc.readUnlock();
704: }
705: }
706: }
707:
708: }
709:
710:
713: private int majorAxis;
714:
715:
718: private float topInset;
719:
720:
723: private float bottomInset;
724:
725:
728: private float leftInset;
729:
730:
733: private boolean estimatedMajorSpan;
734:
735:
738: private float rightInset;
739:
740:
743: private ArrayList childStates;
744:
745:
750: ChildState changing;
751:
752:
756: ChildState minReq;
757:
758:
762: ChildState prefReq;
763:
764:
767: private boolean majorChanged;
768:
769:
773: boolean minorChanged;
774:
775:
779: float majorSpan;
780:
781:
785: float minorSpan;
786:
787:
791: private Runnable flushTask;
792:
793:
796: protected ChildLocator locator;
797:
798:
805: public AsyncBoxView(Element elem, int axis)
806: {
807: super(elem);
808: majorAxis = axis;
809: childStates = new ArrayList();
810: flushTask = new FlushTask();
811: locator = new ChildLocator();
812: minorSpan = Short.MAX_VALUE;
813: }
814:
815:
820: public int getMajorAxis()
821: {
822: return majorAxis;
823: }
824:
825:
831: public int getMinorAxis()
832: {
833: return majorAxis == X_AXIS ? Y_AXIS : X_AXIS;
834: }
835:
836:
843: public View getView(int index)
844: {
845: View view = null;
846: synchronized(childStates)
847: {
848: if ((index >= 0) && (index < childStates.size()))
849: {
850: ChildState cs = (ChildState) childStates.get(index);
851: view = cs.getChildView();
852: }
853: }
854: return view;
855: }
856:
857:
862: public int getViewCount()
863: {
864: synchronized(childStates)
865: {
866: return childStates.size();
867: }
868: }
869:
870:
880: public int getViewIndex(int pos, Position.Bias bias)
881: {
882: int retVal = -1;
883:
884: if (bias == Position.Bias.Backward)
885: pos = Math.max(0, pos - 1);
886:
887:
888:
889: int numChildren = childStates.size();
890: if (numChildren > 0)
891: {
892: for (int i = 0; i < numChildren; ++i)
893: {
894: View child = ((ChildState) childStates.get(i)).getChildView();
895: if (child.getStartOffset() <= pos && child.getEndOffset() > pos)
896: {
897: retVal = i;
898: break;
899: }
900: }
901: }
902: return retVal;
903: }
904:
905:
910: public float getTopInset()
911: {
912: return topInset;
913: }
914:
915:
920: public void setTopInset(float top)
921: {
922: topInset = top;
923: }
924:
925:
930: public float getBottomInset()
931: {
932: return bottomInset;
933: }
934:
935:
940: public void setBottomInset(float bottom)
941: {
942: bottomInset = bottom;
943: }
944:
945:
950: public float getLeftInset()
951: {
952: return leftInset;
953: }
954:
955:
960: public void setLeftInset(float left)
961: {
962: leftInset = left;
963: }
964:
965:
970: public float getRightInset()
971: {
972: return rightInset;
973: }
974:
975:
980: public void setRightInset(float right)
981: {
982: rightInset = right;
983: }
984:
985:
991: protected void loadChildren(ViewFactory f)
992: {
993: Element e = getElement();
994: int n = e.getElementCount();
995: if (n > 0)
996: {
997: View[] added = new View[n];
998: for (int i = 0; i < n; i++)
999: {
1000: added[i] = f.create(e.getElement(i));
1001: }
1002: replace(0, 0, added);
1003: }
1004: }
1005:
1006:
1015: protected float getInsetSpan(int axis)
1016: {
1017: float span;
1018: if (axis == X_AXIS)
1019: span = leftInset + rightInset;
1020: else
1021: span = topInset + bottomInset;
1022: return span;
1023: }
1024:
1025:
1033: protected void setEstimatedMajorSpan(boolean estimated)
1034: {
1035: estimatedMajorSpan = estimated;
1036: }
1037:
1038:
1048: protected boolean getEstimatedMajorSpan()
1049: {
1050: return estimatedMajorSpan;
1051: }
1052:
1053:
1059: protected synchronized void minorRequirementChange(ChildState cs)
1060: {
1061: minorChanged = true;
1062: }
1063:
1064:
1070: protected void majorRequirementChange(ChildState cs, float delta)
1071: {
1072: if (! estimatedMajorSpan)
1073: majorSpan += delta;
1074: majorChanged = true;
1075: }
1076:
1077:
1087: public void setParent(View parent)
1088: {
1089: super.setParent(parent);
1090: if ((parent != null) && (getViewCount() == 0))
1091: {
1092: ViewFactory f = getViewFactory();
1093: loadChildren(f);
1094: }
1095: }
1096:
1097:
1108: public void setSize(float width, float height)
1109: {
1110: float targetSpan;
1111: if (majorAxis == X_AXIS)
1112: targetSpan = height - getTopInset() - getBottomInset();
1113: else
1114: targetSpan = width - getLeftInset() - getRightInset();
1115:
1116: if (targetSpan != minorSpan)
1117: {
1118: minorSpan = targetSpan;
1119:
1120: int n = getViewCount();
1121: LayoutQueue q = getLayoutQueue();
1122: for (int i = 0; i < n; i++)
1123: {
1124: ChildState cs = getChildState(i);
1125: cs.childSizeValid = false;
1126: q.addTask(cs);
1127: }
1128: q.addTask(flushTask);
1129: }
1130: }
1131:
1132:
1142: public void replace(int offset, int length, View[] views)
1143: {
1144: synchronized(childStates)
1145: {
1146: LayoutQueue q = getLayoutQueue();
1147: for (int i = 0; i < length; i++)
1148: childStates.remove(offset);
1149:
1150: for (int i = views.length - 1; i >= 0; i--)
1151: childStates.add(offset, createChildState(views[i]));
1152:
1153:
1154:
1155:
1156:
1157: if (views.length != 0)
1158: {
1159: for (int i = 0; i < views.length; i++)
1160: {
1161: ChildState cs = (ChildState) childStates.get(i + offset);
1162: cs.getChildView().setParent(this);
1163: q.addTask(cs);
1164: }
1165: q.addTask(flushTask);
1166: }
1167: }
1168: }
1169:
1170:
1177: public void paint(Graphics g, Shape s)
1178: {
1179: synchronized (locator)
1180: {
1181: locator.setAllocation(s);
1182: locator.paintChildren(g);
1183: }
1184: }
1185:
1186:
1191: public float getPreferredSpan(int axis)
1192: {
1193: float retVal;
1194: if (majorAxis == axis)
1195: retVal = majorSpan;
1196:
1197: else if (prefReq != null)
1198: {
1199: View child = prefReq.getChildView();
1200: retVal = child.getPreferredSpan(axis);
1201: }
1202:
1203:
1204:
1205: else
1206: {
1207: if (axis == X_AXIS)
1208: retVal = getLeftInset() + getRightInset() + 30;
1209: else
1210: retVal = getTopInset() + getBottomInset() + 30;
1211: }
1212: return retVal;
1213: }
1214:
1215:
1224: public Shape modelToView(int pos, Shape a, Bias b)
1225: throws BadLocationException
1226: {
1227: int index = getViewIndexAtPosition(pos, b);
1228: Shape ca = locator.getChildAllocation(index, a);
1229:
1230: ChildState cs = getChildState(index);
1231: synchronized (cs)
1232: {
1233: View cv = cs.getChildView();
1234: Shape v = cv.modelToView(pos, ca, b);
1235: return v;
1236: }
1237: }
1238:
1239:
1248: public int viewToModel(float x, float y, Shape a, Bias[] b)
1249: {
1250: int pos;
1251: int index;
1252: Shape ca;
1253:
1254: synchronized (locator)
1255: {
1256: index = locator.getViewIndexAtPoint(x, y, a);
1257: ca = locator.getChildAllocation(index, a);
1258: }
1259:
1260: ChildState cs = getChildState(index);
1261: synchronized (cs)
1262: {
1263: View v = cs.getChildView();
1264: pos = v.viewToModel(x, y, ca, b);
1265: }
1266: return pos;
1267: }
1268:
1269:
1278: public Shape getChildAllocation(int index, Shape a)
1279: {
1280: Shape ca = locator.getChildAllocation(index, a);
1281: return ca;
1282: }
1283:
1284:
1294: public float getMaximumSpan(int axis)
1295: {
1296: float max;
1297: if (axis == majorAxis)
1298: max = getPreferredSpan(axis);
1299: else
1300: max = Short.MAX_VALUE;
1301: return max;
1302: }
1303:
1304:
1307: public float getMinimumSpan(int axis)
1308: {
1309: float min;
1310: if (axis == majorAxis)
1311: min = getPreferredSpan(axis);
1312: else
1313: {
1314: if (minReq != null)
1315: {
1316: View child = minReq.getChildView();
1317: min = child.getMinimumSpan(axis);
1318: }
1319: else
1320: {
1321:
1322:
1323: if (axis == X_AXIS)
1324: min = getLeftInset() + getRightInset() + 5;
1325: else
1326: min = getTopInset() + getBottomInset() + 5;
1327: }
1328: }
1329: return min;
1330: }
1331:
1332:
1342: public synchronized void preferenceChanged(View view, boolean width,
1343: boolean height)
1344: {
1345: if (view == null)
1346: getParent().preferenceChanged(this, width, height);
1347: else
1348: {
1349: if (changing != null)
1350: {
1351: View cv = changing.getChildView();
1352: if (cv == view)
1353: {
1354: changing.preferenceChanged(width, height);
1355: return;
1356: }
1357: }
1358: int index = getViewIndexAtPosition(view.getStartOffset(),
1359: Position.Bias.Forward);
1360: ChildState cs = getChildState(index);
1361: cs.preferenceChanged(width, height);
1362: LayoutQueue q = getLayoutQueue();
1363: q.addTask(cs);
1364: q.addTask(flushTask);
1365: }
1366: }
1367:
1368:
1378: protected void updateLayout(DocumentEvent.ElementChange ec,
1379: DocumentEvent e, Shape a)
1380: {
1381: if (ec != null)
1382: {
1383: int index = Math.max(ec.getIndex() - 1, 0);
1384: ChildState cs = getChildState(index);
1385: locator.childChanged(cs);
1386: }
1387: }
1388:
1389:
1390:
1398: protected ChildState getChildState(int index) {
1399: synchronized (childStates)
1400: {
1401: return (ChildState) childStates.get(index);
1402: }
1403: }
1404:
1405:
1411: protected LayoutQueue getLayoutQueue()
1412: {
1413: return LayoutQueue.getDefaultQueue();
1414: }
1415:
1416:
1426: protected synchronized int getViewIndexAtPosition(int pos, Position.Bias b)
1427: {
1428: if (b == Position.Bias.Backward)
1429: pos = Math.max(0, pos - 1);
1430: Element elem = getElement();
1431: return elem.getElementIndex(pos);
1432: }
1433:
1434:
1441: protected ChildState createChildState(View v)
1442: {
1443: return new ChildState(v);
1444: }
1445:
1446:
1450: protected synchronized void flushRequirementChanges()
1451: {
1452: if (majorChanged || minorChanged)
1453: {
1454: View p = getParent();
1455: if (p != null)
1456: {
1457: boolean horizontal;
1458: boolean vertical;
1459: if (majorAxis == X_AXIS)
1460: {
1461: horizontal = majorChanged;
1462: vertical = minorChanged;
1463: }
1464: else
1465: {
1466: vertical = majorChanged;
1467: horizontal = minorChanged;
1468: }
1469:
1470: p.preferenceChanged(this, horizontal, vertical);
1471: majorChanged = false;
1472: minorChanged = false;
1473:
1474: Component c = getContainer();
1475: if (c != null)
1476: c.repaint();
1477: }
1478: }
1479: }
1480: }