1 7 package javax.swing.text; 8 9 import java.util.*; 10 import java.awt.*; 11 import javax.swing.SwingUtilities ; 12 import javax.swing.event.DocumentEvent ; 13 14 33 public class AsyncBoxView extends View { 34 35 42 public AsyncBoxView(Element elem, int axis) { 43 super(elem); 44 stats = new ArrayList(); 45 this.axis = axis; 46 locator = new ChildLocator(); 47 flushTask = new FlushTask(); 48 minorSpan = Short.MAX_VALUE; 49 estimatedMajorSpan = false; 50 } 51 52 57 public int getMajorAxis() { 58 return axis; 59 } 60 61 66 public int getMinorAxis() { 67 return (axis == X_AXIS) ? Y_AXIS : X_AXIS; 68 } 69 70 73 public float getTopInset() { 74 return topInset; 75 } 76 77 82 public void setTopInset(float i) { 83 topInset = i; 84 } 85 86 89 public float getBottomInset() { 90 return bottomInset; 91 } 92 93 98 public void setBottomInset(float i) { 99 bottomInset = i; 100 } 101 102 105 public float getLeftInset() { 106 return leftInset; 107 } 108 109 114 public void setLeftInset(float i) { 115 leftInset = i; 116 } 117 118 121 public float getRightInset() { 122 return rightInset; 123 } 124 125 130 public void setRightInset(float i) { 131 rightInset = i; 132 } 133 134 141 protected float getInsetSpan(int axis) { 142 float margin = (axis == X_AXIS) ? 143 getLeftInset() + getRightInset() : getTopInset() + getBottomInset(); 144 return margin; 145 } 146 147 160 protected void setEstimatedMajorSpan(boolean isEstimated) { 161 estimatedMajorSpan = isEstimated; 162 } 163 164 169 protected boolean getEstimatedMajorSpan() { 170 return estimatedMajorSpan; 171 } 172 173 180 protected ChildState getChildState(int index) { 181 synchronized(stats) { 182 if ((index >= 0) && (index < stats.size())) { 183 return (ChildState) stats.get(index); 184 } 185 return null; 186 } 187 } 188 189 192 protected LayoutQueue getLayoutQueue() { 193 return LayoutQueue.getDefaultQueue(); 194 } 195 196 201 protected ChildState createChildState(View v) { 202 return new ChildState(v); 203 } 204 205 223 protected synchronized void majorRequirementChange(ChildState cs, float delta) { 224 if (estimatedMajorSpan == false) { 225 majorSpan += delta; 226 } 227 majorChanged = true; 228 } 229 230 240 protected synchronized void minorRequirementChange(ChildState cs) { 241 minorChanged = true; 242 } 243 244 248 protected void flushRequirementChanges() { 249 AbstractDocument doc = (AbstractDocument ) getDocument(); 250 try { 251 doc.readLock(); 252 253 View parent = null; 254 boolean horizontal = false; 255 boolean vertical = false; 256 257 synchronized(this) { 258 synchronized(stats) { 261 int n = getViewCount(); 262 if ((n > 0) && (minorChanged || estimatedMajorSpan)) { 263 LayoutQueue q = getLayoutQueue(); 264 ChildState min = getChildState(0); 265 ChildState pref = getChildState(0); 266 float span = 0f; 267 for (int i = 1; i < n; i++) { 268 ChildState cs = getChildState(i); 269 if (minorChanged) { 270 if (cs.min > min.min) { 271 min = cs; 272 } 273 if (cs.pref > pref.pref) { 274 pref = cs; 275 } 276 } 277 if (estimatedMajorSpan) { 278 span += cs.getMajorSpan(); 279 } 280 } 281 282 if (minorChanged) { 283 minRequest = min; 284 prefRequest = pref; 285 } 286 if (estimatedMajorSpan) { 287 majorSpan = span; 288 estimatedMajorSpan = false; 289 majorChanged = true; 290 } 291 } 292 } 293 294 if (majorChanged || minorChanged) { 296 parent = getParent(); 297 if (parent != null) { 298 if (axis == X_AXIS) { 299 horizontal = majorChanged; 300 vertical = minorChanged; 301 } else { 302 vertical = majorChanged; 303 horizontal = minorChanged; 304 } 305 } 306 majorChanged = false; 307 minorChanged = false; 308 } 309 } 310 311 if (parent != null) { 314 parent.preferenceChanged(this, horizontal, vertical); 315 316 Component c = getContainer(); 318 if (c != null) { 319 c.repaint(); 320 } 321 } 322 } finally { 323 doc.readUnlock(); 324 } 325 } 326 327 339 public void replace(int offset, int length, View [] views) { 340 synchronized(stats) { 341 for (int i = 0; i < length; i++) { 343 ChildState cs = (ChildState)stats.remove(offset); 344 float csSpan = cs.getMajorSpan(); 345 346 cs.getChildView().setParent(null); 347 if (csSpan != 0) { 348 majorRequirementChange(cs, -csSpan); 349 } 350 } 351 352 LayoutQueue q = getLayoutQueue(); 354 if (views != null) { 355 for (int i = 0; i < views.length; i++) { 356 ChildState s = createChildState(views[i]); 357 stats.add(offset + i, s); 358 q.addTask(s); 359 } 360 } 361 362 q.addTask(flushTask); 364 } 365 } 366 367 385 protected void loadChildren(ViewFactory f) { 386 Element e = getElement(); 387 int n = e.getElementCount(); 388 if (n > 0) { 389 View [] added = new View [n]; 390 for (int i = 0; i < n; i++) { 391 added[i] = f.create(e.getElement(i)); 392 } 393 replace(0, 0, added); 394 } 395 } 396 397 406 protected synchronized int getViewIndexAtPosition(int pos, Position.Bias b) { 407 boolean isBackward = (b == Position.Bias.Backward); 408 pos = (isBackward) ? Math.max(0, pos - 1) : pos; 409 Element elem = getElement(); 410 return elem.getElementIndex(pos); 411 } 412 413 427 protected void updateLayout(DocumentEvent.ElementChange ec, 428 DocumentEvent e, Shape a) { 429 if (ec != null) { 430 int index = Math.max(ec.getIndex() - 1, 0); 435 ChildState cs = getChildState(index); 436 locator.childChanged(cs); 437 } 438 } 439 440 442 457 public void setParent(View parent) { 458 super.setParent(parent); 459 if ((parent != null) && (getViewCount() == 0)) { 460 ViewFactory f = getViewFactory(); 461 loadChildren(f); 462 } 463 } 464 465 477 public synchronized void preferenceChanged(View child, boolean width, boolean height) { 478 if (child == null) { 479 getParent().preferenceChanged(this, width, height); 480 } else { 481 if (changing != null) { 482 View cv = changing.getChildView(); 483 if (cv == child) { 484 changing.preferenceChanged(width, height); 487 return; 488 } 489 } 490 int index = getViewIndex(child.getStartOffset(), 491 Position.Bias.Forward); 492 ChildState cs = getChildState(index); 493 cs.preferenceChanged(width, height); 494 LayoutQueue q = getLayoutQueue(); 495 q.addTask(cs); 496 q.addTask(flushTask); 497 } 498 } 499 500 513 public void setSize(float width, float height) { 514 setSpanOnAxis(X_AXIS, width); 515 setSpanOnAxis(Y_AXIS, height); 516 } 517 518 525 float getSpanOnAxis(int axis) { 526 if (axis == getMajorAxis()) { 527 return majorSpan; 528 } 529 return minorSpan; 530 } 531 532 543 void setSpanOnAxis(int axis, float span) { 544 float margin = getInsetSpan(axis); 545 if (axis == getMinorAxis()) { 546 float targetSpan = span - margin; 547 if (targetSpan != minorSpan) { 548 minorSpan = targetSpan; 549 550 int n = getViewCount(); 553 if (n != 0) { 554 LayoutQueue q = getLayoutQueue(); 555 for (int i = 0; i < n; i++) { 556 ChildState cs = getChildState(i); 557 cs.childSizeValid = false; 558 q.addTask(cs); 559 } 560 q.addTask(flushTask); 561 } 562 } 563 } else { 564 if (estimatedMajorSpan) { 568 majorSpan = span - margin; 569 } 570 } 571 } 572 573 591 public void paint(Graphics g, Shape alloc) { 592 synchronized (locator) { 593 locator.setAllocation(alloc); 594 locator.paintChildren(g); 595 } 596 } 597 598 609 public float getPreferredSpan(int axis) { 610 float margin = getInsetSpan(axis); 611 if (axis == this.axis) { 612 return majorSpan + margin; 613 } 614 if (prefRequest != null) { 615 View child = prefRequest.getChildView(); 616 return child.getPreferredSpan(axis) + margin; 617 } 618 619 return margin + 30; 621 } 622 623 634 public float getMinimumSpan(int axis) { 635 if (axis == this.axis) { 636 return getPreferredSpan(axis); 637 } 638 if (minRequest != null) { 639 View child = minRequest.getChildView(); 640 return child.getMinimumSpan(axis); 641 } 642 643 if (axis == X_AXIS) { 645 return getLeftInset() + getRightInset() + 5; 646 } else { 647 return getTopInset() + getBottomInset() + 5; 648 } 649 } 650 651 662 public float getMaximumSpan(int axis) { 663 if (axis == this.axis) { 664 return getPreferredSpan(axis); 665 } 666 return Integer.MAX_VALUE; 667 } 668 669 670 678 public int getViewCount() { 679 synchronized(stats) { 680 return stats.size(); 681 } 682 } 683 684 691 public View getView(int n) { 692 ChildState cs = getChildState(n); 693 if (cs != null) { 694 return cs.getChildView(); 695 } 696 return null; 697 } 698 699 710 public Shape getChildAllocation(int index, Shape a) { 711 Shape ca = locator.getChildAllocation(index, a); 712 return ca; 713 } 714 715 726 public int getViewIndex(int pos, Position.Bias b) { 727 return getViewIndexAtPosition(pos, b); 728 } 729 730 745 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { 746 int index = getViewIndex(pos, b); 747 Shape ca = locator.getChildAllocation(index, a); 748 749 ChildState cs = getChildState(index); 753 synchronized (cs) { 754 View cv = cs.getChildView(); 755 Shape v = cv.modelToView(pos, ca, b); 756 return v; 757 } 758 } 759 760 781 public int viewToModel(float x, float y, Shape a, Position.Bias [] biasReturn) { 782 int pos; int index; Shape ca; 786 synchronized (locator) { 792 index = locator.getViewIndexAtPoint(x, y, a); 793 ca = locator.getChildAllocation(index, a); 794 } 795 796 ChildState cs = getChildState(index); 800 synchronized (cs) { 801 View v = cs.getChildView(); 802 pos = v.viewToModel(x, y, ca, biasReturn); 803 } 804 return pos; 805 } 806 807 830 public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, 831 int direction, 832 Position.Bias [] biasRet) 833 throws BadLocationException { 834 return Utilities.getNextVisualPositionFrom( 835 this, pos, b, a, direction, biasRet); 836 } 837 838 840 844 int axis; 845 846 849 java.util.List stats; 850 851 857 float majorSpan; 858 859 862 boolean estimatedMajorSpan; 863 864 869 float minorSpan; 870 871 876 protected ChildLocator locator; 877 878 float topInset; 879 float bottomInset; 880 float leftInset; 881 float rightInset; 882 883 ChildState minRequest; 884 ChildState prefRequest; 885 boolean majorChanged; 886 boolean minorChanged; 887 Runnable flushTask; 888 889 896 ChildState changing; 897 898 906 public class ChildLocator { 907 908 911 public ChildLocator() { 912 lastAlloc = new Rectangle(); 913 childAlloc = new Rectangle(); 914 } 915 916 923 public synchronized void childChanged(ChildState cs) { 924 if (lastValidOffset == null) { 925 lastValidOffset = cs; 926 } else if (cs.getChildView().getStartOffset() < 927 lastValidOffset.getChildView().getStartOffset()) { 928 lastValidOffset = cs; 929 } 930 } 931 932 935 public synchronized void paintChildren(Graphics g) { 936 Rectangle clip = g.getClipBounds(); 937 float targetOffset = (axis == X_AXIS) ? 938 clip.x - lastAlloc.x : clip.y - lastAlloc.y; 939 int index = getViewIndexAtVisualOffset(targetOffset); 940 int n = getViewCount(); 941 float offs = getChildState(index).getMajorOffset(); 942 for (int i = index; i < n; i++) { 943 ChildState cs = getChildState(i); 944 cs.setMajorOffset(offs); 945 Shape ca = getChildAllocation(i); 946 if (intersectsClip(ca, clip)) { 947 synchronized (cs) { 948 View v = cs.getChildView(); 949 v.paint(g, ca); 950 } 951 } else { 952 break; 954 } 955 offs += cs.getMajorSpan(); 956 } 957 } 958 959 964 public synchronized Shape getChildAllocation(int index, Shape a) { 965 if (a == null) { 966 return null; 967 } 968 setAllocation(a); 969 ChildState cs = getChildState(index); 970 if (lastValidOffset == null) { 971 lastValidOffset = getChildState(0); 972 } 973 if (cs.getChildView().getStartOffset() > 974 lastValidOffset.getChildView().getStartOffset()) { 975 updateChildOffsetsToIndex(index); 977 } 978 Shape ca = getChildAllocation(index); 979 return ca; 980 } 981 982 996 public int getViewIndexAtPoint(float x, float y, Shape a) { 997 setAllocation(a); 998 float targetOffset = (axis == X_AXIS) ? x - lastAlloc.x : y - lastAlloc.y; 999 int index = getViewIndexAtVisualOffset(targetOffset); 1000 return index; 1001 } 1002 1003 1008 protected Shape getChildAllocation(int index) { 1009 ChildState cs = getChildState(index); 1010 if (! cs.isLayoutValid()) { 1011 cs.run(); 1012 } 1013 if (axis == X_AXIS) { 1014 childAlloc.x = lastAlloc.x + (int) cs.getMajorOffset(); 1015 childAlloc.y = lastAlloc.y + (int) cs.getMinorOffset(); 1016 childAlloc.width = (int) cs.getMajorSpan(); 1017 childAlloc.height = (int) cs.getMinorSpan(); 1018 } else { 1019 childAlloc.y = lastAlloc.y + (int) cs.getMajorOffset(); 1020 childAlloc.x = lastAlloc.x + (int) cs.getMinorOffset(); 1021 childAlloc.height = (int) cs.getMajorSpan(); 1022 childAlloc.width = (int) cs.getMinorSpan(); 1023 } 1024 childAlloc.x += (int)getLeftInset(); 1025 childAlloc.y += (int)getRightInset(); 1026 return childAlloc; 1027 } 1028 1029 1034 protected void setAllocation(Shape a) { 1035 if (a instanceof Rectangle) { 1036 lastAlloc.setBounds((Rectangle) a); 1037 } else { 1038 lastAlloc.setBounds(a.getBounds()); 1039 } 1040 setSize(lastAlloc.width, lastAlloc.height); 1041 } 1042 1043 1053 protected int getViewIndexAtVisualOffset(float targetOffset) { 1054 int n = getViewCount(); 1055 if (n > 0) { 1056 boolean lastValid = (lastValidOffset != null); 1057 1058 if (lastValidOffset == null) { 1059 lastValidOffset = getChildState(0); 1060 } 1061 if (targetOffset > majorSpan) { 1062 if (!lastValid) { 1064 return 0; 1065 } 1066 int pos = lastValidOffset.getChildView().getStartOffset(); 1067 int index = getViewIndex(pos, Position.Bias.Forward); 1068 return index; 1069 } else if (targetOffset > lastValidOffset.getMajorOffset()) { 1070 return updateChildOffsets(targetOffset); 1072 } else { 1073 float offs = 0f; 1076 for (int i = 0; i < n; i++) { 1077 ChildState cs = getChildState(i); 1078 float nextOffs = offs + cs.getMajorSpan(); 1079 if (targetOffset < nextOffs) { 1080 return i; 1081 } 1082 offs = nextOffs; 1083 } 1084 } 1085 } 1086 return n - 1; 1087 } 1088 1089 1093 int updateChildOffsets(float targetOffset) { 1094 int n = getViewCount(); 1095 int targetIndex = n - 1;; 1096 int pos = lastValidOffset.getChildView().getStartOffset(); 1097 int startIndex = getViewIndex(pos, Position.Bias.Forward); 1098 float start = lastValidOffset.getMajorOffset(); 1099 float lastOffset = start; 1100 for (int i = startIndex; i < n; i++) { 1101 ChildState cs = getChildState(i); 1102 cs.setMajorOffset(lastOffset); 1103 lastOffset += cs.getMajorSpan(); 1104 if (targetOffset < lastOffset) { 1105 targetIndex = i; 1106 lastValidOffset = cs; 1107 break; 1108 } 1109 } 1110 1111 return targetIndex; 1112 } 1113 1114 1118 void updateChildOffsetsToIndex(int index) { 1119 int pos = lastValidOffset.getChildView().getStartOffset(); 1120 int startIndex = getViewIndex(pos, Position.Bias.Forward); 1121 float lastOffset = lastValidOffset.getMajorOffset(); 1122 for (int i = startIndex; i <= index; i++) { 1123 ChildState cs = getChildState(i); 1124 cs.setMajorOffset(lastOffset); 1125 lastOffset += cs.getMajorSpan(); 1126 } 1127 } 1128 1129 boolean intersectsClip(Shape childAlloc, Rectangle clip) { 1130 Rectangle cs = (childAlloc instanceof Rectangle) ? 1131 (Rectangle) childAlloc : childAlloc.getBounds(); 1132 if (cs.intersects(clip)) { 1133 return lastAlloc.intersects(cs); 1136 } 1137 return false; 1138 } 1139 1140 1144 protected ChildState lastValidOffset; 1145 1146 1150 protected Rectangle lastAlloc; 1151 1152 1156 protected Rectangle childAlloc; 1157 } 1158 1159 1170 public class ChildState implements Runnable { 1171 1172 1177 public ChildState(View v) { 1178 child = v; 1179 minorValid = false; 1180 majorValid = false; 1181 childSizeValid = false; 1182 child.setParent(AsyncBoxView.this); 1183 } 1184 1185 1188 public View getChildView() { 1189 return child; 1190 } 1191 1192 1213 public void run () { 1214 AbstractDocument doc = (AbstractDocument ) getDocument(); 1215 try { 1216 doc.readLock(); 1217 if (minorValid && majorValid && childSizeValid) { 1218 return; 1220 } 1221 if (child.getParent() == AsyncBoxView.this) { 1222 synchronized(AsyncBoxView.this) { 1227 changing = this; 1228 } 1229 updateChild(); 1230 synchronized(AsyncBoxView.this) { 1231 changing = null; 1232 } 1233 1234 updateChild(); 1238 } 1239 } finally { 1240 doc.readUnlock(); 1241 } 1242 } 1243 1244 void updateChild() { 1245 boolean minorUpdated = false; 1246 synchronized(this) { 1247 if (! minorValid) { 1248 int minorAxis = getMinorAxis(); 1249 min = child.getMinimumSpan(minorAxis); 1250 pref = child.getPreferredSpan(minorAxis); 1251 max = child.getMaximumSpan(minorAxis); 1252 minorValid = true; 1253 minorUpdated = true; 1254 } 1255 } 1256 if (minorUpdated) { 1257 minorRequirementChange(this); 1258 } 1259 1260 boolean majorUpdated = false; 1261 float delta = 0.0f; 1262 synchronized(this) { 1263 if (! majorValid) { 1264 float old = span; 1265 span = child.getPreferredSpan(axis); 1266 delta = span - old; 1267 majorValid = true; 1268 majorUpdated = true; 1269 } 1270 } 1271 if (majorUpdated) { 1272 majorRequirementChange(this, delta); 1273 locator.childChanged(this); 1274 } 1275 1276 synchronized(this) { 1277 if (! childSizeValid) { 1278 float w; 1279 float h; 1280 if (axis == X_AXIS) { 1281 w = span; 1282 h = getMinorSpan(); 1283 } else { 1284 w = getMinorSpan(); 1285 h = span; 1286 } 1287 childSizeValid = true; 1288 child.setSize(w, h); 1289 } 1290 } 1291 1292 } 1293 1294 1297 public float getMinorSpan() { 1298 if (max < minorSpan) { 1299 return max; 1300 } 1301 return Math.max(min, minorSpan); 1303 } 1304 1305 1308 public float getMinorOffset() { 1309 if (max < minorSpan) { 1310 float align = child.getAlignment(getMinorAxis()); 1312 return ((minorSpan - max) * align); 1313 } 1314 return 0f; 1315 } 1316 1317 1320 public float getMajorSpan() { 1321 return span; 1322 } 1323 1324 1327 public float getMajorOffset() { 1328 return offset; 1329 } 1330 1331 1336 public void setMajorOffset(float offs) { 1337 offset = offs; 1338 } 1339 1340 1347 public void preferenceChanged(boolean width, boolean height) { 1348 if (axis == X_AXIS) { 1349 if (width) { 1350 majorValid = false; 1351 } 1352 if (height) { 1353 minorValid = false; 1354 } 1355 } else { 1356 if (width) { 1357 minorValid = false; 1358 } 1359 if (height) { 1360 majorValid = false; 1361 } 1362 } 1363 childSizeValid = false; 1364 } 1365 1366 1369 public boolean isLayoutValid() { 1370 return (minorValid && majorValid && childSizeValid); 1371 } 1372 1373 private float min; 1375 private float pref; 1376 private float max; 1377 private float align; 1378 private boolean minorValid; 1379 1380 private float span; 1382 private float offset; 1383 private boolean majorValid; 1384 1385 private View child; 1386 private boolean childSizeValid; 1387 } 1388 1389 1392 class FlushTask implements Runnable { 1393 1394 public void run() { 1395 flushRequirementChanges(); 1396 } 1397 1398 } 1399 1400} 1401 | Popular Tags |