1 19 20 package org.netbeans.modules.form.layoutdesign; 21 22 import java.util.*; 23 24 36 37 class LayoutFeeder implements LayoutConstants { 38 39 boolean imposeSize; 40 boolean optimizeStructure; 41 42 private LayoutModel layoutModel; 43 private LayoutOperations operations; 44 45 private LayoutDragger dragger; 46 private IncludeDesc[] originalPositions1 = new IncludeDesc[DIM_COUNT]; 47 private IncludeDesc[] originalPositions2 = new IncludeDesc[DIM_COUNT]; 48 private boolean[] originalLPositionsFixed = new boolean[DIM_COUNT]; 49 private boolean[] originalTPositionsFixed = new boolean[DIM_COUNT]; 50 private LayoutDragger.PositionDef[] newPositions = new LayoutDragger.PositionDef[DIM_COUNT]; 51 private LayoutInterval[] addingIntervals; private boolean[] becomeResizing = new boolean[DIM_COUNT]; 53 54 private int dimension; 56 private LayoutInterval addingInterval; 57 private LayoutRegion addingSpace; 58 private boolean solveOverlap; 59 private boolean originalLPosFixed; 60 private boolean originalTPosFixed; 61 62 private int aEdge; 64 private LayoutInterval aSnappedParallel; 65 private LayoutInterval aSnappedNextTo; 66 67 private static class IncludeDesc { 68 LayoutInterval parent; 69 int index = -1; boolean newSubGroup; LayoutInterval neighbor; LayoutInterval snappedParallel; LayoutInterval snappedNextTo; int alignment; boolean fixedPosition; int distance = Integer.MAX_VALUE; 77 int ortDistance = Integer.MAX_VALUE; 78 79 boolean snapped() { 80 return snappedNextTo != null || snappedParallel != null; 81 } 82 } 83 84 86 LayoutFeeder(LayoutOperations operations, LayoutDragger dragger, LayoutInterval[] addingIntervals) { 87 this.layoutModel = operations.getModel(); 88 this.operations = operations; 89 this.dragger = dragger; 90 this.addingIntervals = addingIntervals; 91 92 for (int dim=0; dim < DIM_COUNT; dim++) { 93 dimension = dim; 94 if (dragger.isResizing()) { 95 LayoutInterval adding = addingIntervals[dim]; 96 if (dragger.isResizing(dim)) { 97 IncludeDesc pos = findOutCurrentPosition( 98 adding, dim, dragger.getResizingEdge(dim)^1); 99 LayoutDragger.PositionDef newPos = dragger.getPositions()[dim]; 100 if ((newPos == null || !newPos.snapped) && !pos.snapped()) { 101 pos.alignment = LayoutInterval.getEffectiveAlignment(adding); 102 } 103 originalPositions1[dim] = pos; 104 newPositions[dim] = newPos; 105 becomeResizing[dim] = checkResizing(); } 107 else { int alignment = DEFAULT; 109 IncludeDesc pos1 = findOutCurrentPosition(adding, dim, alignment); 110 originalPositions1[dim] = pos1; 111 alignment = pos1.alignment; 112 if (alignment == LEADING || alignment == TRAILING) { 113 IncludeDesc pos2 = findOutCurrentPosition(adding, dim, alignment^1); 114 if (pos2.snapped()) { 115 originalPositions2[dim] = pos2; 116 } } 118 } 119 originalLPositionsFixed[dim] = isFixedRelativePosition(adding, LEADING); 120 originalTPositionsFixed[dim] = isFixedRelativePosition(adding, TRAILING); 121 } 122 else newPositions[dim] = dragger.getPositions()[dim]; 123 } 124 } 125 126 void add() { 127 int overlapDim = getDimensionSolvingOverlap(newPositions); 128 129 for (int dim=overlapDim, dc=0; dc < DIM_COUNT; dim^=1, dc++) { 130 dimension = dim; 131 addingInterval = addingIntervals[dim]; 132 addingSpace = dragger.getMovingSpace(); 133 addingInterval.setCurrentSpace(addingSpace); 134 solveOverlap = overlapDim == dim; 135 IncludeDesc originalPos1 = originalPositions1[dim]; 136 IncludeDesc originalPos2 = originalPositions2[dim]; 137 correctNeighborInSequence(originalPos1); 138 correctNeighborInSequence(originalPos2); 139 140 if (dragger.isResizing()) { 141 originalLPosFixed = originalLPositionsFixed[dim]; 142 originalTPosFixed = originalTPositionsFixed[dim]; 143 if (dragger.isResizing(dim)) { 144 layoutModel.setIntervalSize(addingInterval, 145 becomeResizing[dim] ? NOT_EXPLICITLY_DEFINED : USE_PREFERRED_SIZE, 146 addingSpace.size(dim), 147 becomeResizing[dim] ? Short.MAX_VALUE : USE_PREFERRED_SIZE); 148 } 149 } 150 151 LayoutDragger.PositionDef newPos = newPositions[dim]; 152 if (newPos != null && (newPos.alignment == CENTER || newPos.alignment == BASELINE)) { 153 aEdge = newPos.alignment; 155 aSnappedParallel = newPos.interval; 156 addSimplyAligned(); 157 continue; 158 } 159 if (dragger.isResizing() && (originalPos1.alignment == CENTER || originalPos1.alignment == BASELINE)) { 160 aEdge = originalPos1.alignment; 161 aSnappedParallel = originalPos1.snappedParallel; 162 addSimplyAligned(); 163 continue; 164 } 165 166 IncludeDesc inclusion1 = null; 168 IncludeDesc inclusion2 = null; 169 170 List inclusions = new LinkedList(); 171 boolean preserveOriginal = false; 172 173 if (dragger.isResizing(dim^1)) { 175 aEdge = originalPos1.alignment; 176 aSnappedParallel = originalPos1.snappedParallel; 177 aSnappedNextTo = originalPos1.snappedNextTo; 178 } 179 else if (newPos != null) { 181 aEdge = newPos.alignment; 182 aSnappedParallel = !newPos.nextTo ? newPos.interval : null; 183 aSnappedNextTo = newPos.snapped && newPos.nextTo ? newPos.interval : null; 184 preserveOriginal = dragger.isResizing(dim); 186 } 187 else if (dragger.isResizing(dim)) { 190 aEdge = originalPos1.alignment; 191 aSnappedParallel = originalPos1.snappedParallel; 192 aSnappedNextTo = originalPos1.snappedNextTo; 193 preserveOriginal = true; 194 } 195 else { 197 aEdge = DEFAULT; 198 aSnappedParallel = aSnappedNextTo = null; 199 } 200 201 LayoutInterval root = dragger.getTargetContainer().getLayoutRoot(dim); 202 analyzeParallel(root, inclusions); 203 204 if (inclusions.isEmpty()) { assert aSnappedParallel != null; 207 if (originalPos1 != null && originalPos1.alignment == aEdge) 208 inclusions.add(originalPos1); 209 else 210 addAligningInclusion(inclusions); 211 } 212 else { 213 IncludeDesc preferred = addAligningInclusion(inclusions); if (inclusions.size() > 1) { 215 if (preferred == null || (preserveOriginal && originalPos1.alignment == aEdge)) 216 preferred = originalPos1; 217 mergeParallelInclusions(inclusions, preferred, preserveOriginal); 218 assert inclusions.size() == 1; 219 } 220 } 221 222 IncludeDesc found = (IncludeDesc) inclusions.get(0); 223 inclusions.clear(); 224 if (preserveOriginal) { inclusion1 = originalPos1; 226 if (found != originalPos1) { 227 if (newPos != null) 228 inclusion2 = found; 229 if (found.parent == originalPos1.parent && found.newSubGroup) 230 originalPos1.newSubGroup = true; 231 } 232 } 233 else { 234 inclusion1 = found; 235 if (dragger.isResizing(dim^1) && (newPos != null || originalPos2 != null)) { 238 if (newPos != null) { assert dragger.isResizing(dim); 240 aEdge = newPos.alignment; 241 aSnappedParallel = !newPos.nextTo ? newPos.interval : null; 242 aSnappedNextTo = newPos.snapped && newPos.nextTo ? newPos.interval : null; 243 } 244 else { assert !dragger.isResizing(dim); 246 aEdge = originalPos2.alignment; 247 aSnappedParallel = originalPos2.snappedParallel; 248 aSnappedNextTo = originalPos2.snappedNextTo; 249 } 250 analyzeParallel(root, inclusions); 252 253 if (inclusions.isEmpty()) { assert aSnappedParallel != null; 255 if (originalPos2 != null && originalPos2.alignment == aEdge) 256 inclusions.add(originalPos2); 257 else 258 addAligningInclusion(inclusions); 259 } 260 else { 261 IncludeDesc preferred = addAligningInclusion(inclusions); 262 if (inclusions.size() > 1) { 263 if (preferred == null) 264 preferred = originalPos2 != null ? originalPos2 : originalPos1; 265 mergeParallelInclusions(inclusions, preferred, false); 266 assert inclusions.size() == 1; 267 } 268 } 269 inclusion2 = (IncludeDesc) inclusions.get(0); 270 inclusions.clear(); 271 } 272 } 273 274 if (!mergeSequentialInclusions(inclusion1, inclusion2)) 275 inclusion2 = null; 276 277 addInterval(inclusion1, inclusion2); 278 } 279 } 280 281 private static IncludeDesc findOutCurrentPosition(LayoutInterval interval, int dimension, int alignment) { 282 LayoutInterval parent = interval.getParent(); 283 int nonEmptyCount = LayoutInterval.getCount(parent, LayoutRegion.ALL_POINTS, true); 284 285 IncludeDesc iDesc = new IncludeDesc(); 286 287 if (parent.isSequential() && nonEmptyCount > 1) { 288 if (alignment < 0) 289 alignment = LEADING; 290 if (nonEmptyCount == 2) { iDesc.parent = parent.getParent(); 293 int index = 0; 294 for (int i=parent.getSubIntervalCount()-1; i >= 0; i--) { 295 LayoutInterval li = parent.getSubInterval(i); 296 if (li == interval) { 297 index = i; 298 } 299 else if (!li.isEmptySpace()) { 300 iDesc.neighbor = li; iDesc.index = index; 302 break; 303 } 304 } 305 } 306 else { iDesc.parent = parent; 308 iDesc.index = parent.indexOf(interval); 309 } 310 } 311 else { if (parent.isSequential()) { 313 parent = parent.getParent(); nonEmptyCount = LayoutInterval.getCount(parent, LayoutRegion.ALL_POINTS, true); 315 if (alignment < 0) 316 alignment = LEADING; 317 } 318 else { 319 int currentAlign = interval.getAlignment(); 320 if (alignment < 0 || (currentAlign != LEADING && currentAlign != TRAILING)) 321 alignment = currentAlign; 322 } 323 if (nonEmptyCount <= 2 && parent.getParent() != null) { 324 LayoutInterval subGroup = parent; 326 parent = parent.getParent(); 327 if (parent.isSequential()) { 328 boolean ortOverlap = false; 329 for (Iterator it=parent.getSubIntervals(); it.hasNext(); ) { 330 LayoutInterval li = (LayoutInterval) it.next(); 331 if (!li.isEmptySpace() && !li.isParentOf(interval) 332 && LayoutRegion.overlap(interval.getCurrentSpace(), li.getCurrentSpace(), dimension^1, 0)) 333 { ortOverlap = true; 335 break; 336 } 337 } 338 if (ortOverlap) { iDesc.newSubGroup = true; 340 iDesc.index = parent.indexOf(subGroup); 341 } 342 else parent = parent.getParent(); 344 } 345 iDesc.parent = parent; 346 } 347 else iDesc.parent = parent; } 349 350 if (alignment == LEADING || alignment == TRAILING) { 351 iDesc.fixedPosition = isFixedRelativePosition(interval, alignment); 352 } 353 354 iDesc.snappedParallel = findAlignedInterval(interval, dimension, alignment); 355 356 if (iDesc.snappedParallel == null && (alignment == LEADING || alignment == TRAILING)) { 358 LayoutInterval gap = LayoutInterval.getNeighbor(interval, alignment, false, true, false); 359 if (gap != null && LayoutInterval.isFixedDefaultPadding(gap)) { 360 LayoutInterval prev = LayoutInterval.getDirectNeighbor(gap, alignment^1, true); 361 if (prev == interval || LayoutInterval.isPlacedAtBorder(interval, prev, dimension, alignment)) { 362 LayoutInterval next = LayoutInterval.getNeighbor(gap, alignment, true, true, false); 363 if (next != null) { 364 if (next.getParent() == gap.getParent() 365 || next.getCurrentSpace().positions[dimension][alignment^1] 366 == gap.getParent().getCurrentSpace().positions[dimension][alignment]) 367 { iDesc.snappedNextTo = next; 369 } 370 } 371 else { next = LayoutInterval.getRoot(interval); 373 if (LayoutInterval.isPlacedAtBorder(gap.getParent(), next, dimension, alignment)) 374 iDesc.snappedNextTo = next; 375 } 376 } 377 } 378 } 379 380 iDesc.alignment = alignment; 381 return iDesc; 382 } 383 384 private static boolean isFixedRelativePosition(LayoutInterval interval, int edge) { 385 assert edge == LEADING || edge == TRAILING; 386 LayoutInterval parent = interval.getParent(); 387 if (parent == null) 388 return true; 389 if (parent.isSequential()) { 390 LayoutInterval li = LayoutInterval.getDirectNeighbor(interval, edge, false); 391 if (li != null) 392 return !LayoutInterval.wantResize(li); 393 else { 394 interval = parent; 395 parent = interval.getParent(); 396 } 397 } 398 if (!LayoutInterval.isAlignedAtBorder(interval, parent, edge) 399 && LayoutInterval.contentWantResize(parent)) 400 return false; 401 402 return isFixedRelativePosition(parent, edge); 403 } 404 405 private static LayoutInterval findAlignedInterval(LayoutInterval interval, 406 int dimension, 407 int alignment) 408 { 409 LayoutInterval parent; 410 LayoutInterval alignedInterval = null; 411 boolean indent = false; 416 parent = interval.getParent(); 417 if ((alignment == LEADING || alignment == TRAILING) 418 && parent.isSequential() 419 && LayoutInterval.getCount(parent, -1, true) == 1) 420 { LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(interval, alignment, false); 422 if (neighbor != null && neighbor.isEmptySpace() && !LayoutInterval.canResize(neighbor) 423 && LayoutInterval.getCount(parent.getParent(), LayoutRegion.ALL_POINTS, true) == 2) 424 { indent = true; 426 } 427 } 428 429 do { 430 parent = LayoutInterval.getFirstParent(interval, PARALLEL); 431 if (!indent) { 432 boolean aligned = alignment == LEADING || alignment == TRAILING ? 433 LayoutInterval.isAlignedAtBorder(interval, parent, alignment) : 434 interval.getParent() == parent && interval.getAlignment() == alignment; 435 if (!aligned) 436 return null; 437 if (parent.getParent() == null) 438 return parent; } 440 441 for (Iterator it=parent.getSubIntervals(); it.hasNext(); ) { 442 LayoutInterval sub = (LayoutInterval) it.next(); 443 if (!sub.isEmptySpace() && sub != interval && !sub.isParentOf(interval)) { 444 if (alignment == LEADING || alignment == TRAILING) { 445 LayoutInterval li = LayoutUtils.getOutermostComponent(sub, dimension, alignment); 446 if (LayoutInterval.isAlignedAtBorder(li, parent, alignment) 447 || LayoutInterval.isPlacedAtBorder(li, parent, dimension, alignment)) 448 { LayoutInterval p = LayoutInterval.getFirstParent(li, PARALLEL); 450 while (p != parent) { 451 li = p; 452 p = LayoutInterval.getFirstParent(li, PARALLEL); 453 } 454 alignedInterval = li; 455 } 456 else continue; } 458 else alignedInterval = sub; 459 break; 460 } 461 } 462 463 if (indent) 464 return alignedInterval; 465 466 interval = parent; 467 } 468 while (alignedInterval == null); 469 470 return parent.getSubIntervalCount() > 2 ? parent : alignedInterval; 471 } 472 473 481 private static void correctNeighborInSequence(IncludeDesc iDesc) { 482 if (iDesc != null && iDesc.neighbor != null && iDesc.neighbor.getParent().isSequential()) { 483 assert iDesc.parent == iDesc.neighbor.getParent().getParent(); 484 iDesc.parent = iDesc.neighbor.getParent(); 485 iDesc.neighbor = null; 486 } 487 } 488 489 492 private int getDimensionSolvingOverlap(LayoutDragger.PositionDef[] positions) { 493 if (dragger.isResizing(HORIZONTAL) && !dragger.isResizing(VERTICAL)) { 494 return HORIZONTAL; 495 } 496 if ((dragger.isResizing(VERTICAL) && !dragger.isResizing(HORIZONTAL)) 497 || (positions[HORIZONTAL] != null && positions[HORIZONTAL].snapped && (positions[VERTICAL] == null || !positions[VERTICAL].snapped)) 498 || (positions[VERTICAL] != null && !positions[VERTICAL].nextTo && positions[VERTICAL].snapped 499 && (positions[VERTICAL].interval.getParent() == null) 500 && !existsComponentPlacedAtBorder(positions[VERTICAL].interval, VERTICAL, positions[VERTICAL].alignment))) { 501 return VERTICAL; 502 } 503 if (positions[VERTICAL] != null && positions[VERTICAL].nextTo && positions[VERTICAL].snapped 504 && (positions[VERTICAL].interval.getParent() == null)) { 505 int alignment = positions[VERTICAL].alignment; 506 int[][] overlapSides = overlappingGapSides(dragger.getTargetContainer().getLayoutRoot(HORIZONTAL), 507 dragger.getMovingSpace()); 508 if (((alignment == LEADING) || (alignment == TRAILING)) 509 && (overlapSides[VERTICAL][1-alignment] != 0) 510 && (overlapSides[VERTICAL][alignment] == 0)) { 511 return VERTICAL; 512 } 513 } 514 if ((positions[HORIZONTAL] == null || !positions[HORIZONTAL].snapped) 515 && (positions[VERTICAL] == null || !positions[VERTICAL].snapped)) { 516 boolean[] overlapDim = overlappingGapDimensions(dragger.getTargetContainer().getLayoutRoot(HORIZONTAL), 517 dragger.getMovingSpace()); 518 if (overlapDim[VERTICAL] && !overlapDim[HORIZONTAL]) { 519 return VERTICAL; 520 } 521 } 522 return HORIZONTAL; 523 } 524 525 533 private static boolean existsComponentPlacedAtBorder(LayoutInterval interval, int dimension, int alignment) { 534 Iterator iter = interval.getSubIntervals(); 535 while (iter.hasNext()) { 536 LayoutInterval subInterval = (LayoutInterval)iter.next(); 537 if (LayoutInterval.isPlacedAtBorder(interval, dimension, alignment)) { 538 if (subInterval.isComponent()) { 539 return true; 540 } else if (subInterval.isGroup()) { 541 if (existsComponentPlacedAtBorder(subInterval, dimension, alignment)) { 542 return true; 543 } 544 } 545 } 546 } 547 return false; 548 } 549 550 557 private static void fillOverlappingComponents(List overlaps, LayoutInterval group, LayoutRegion region) { 558 Iterator iter = group.getSubIntervals(); 559 while (iter.hasNext()) { 560 LayoutInterval subInterval = (LayoutInterval)iter.next(); 561 if (subInterval.isGroup()) { 562 fillOverlappingComponents(overlaps, subInterval, region); 563 } else if (subInterval.isComponent()) { 564 LayoutComponent component = subInterval.getComponent(); 565 LayoutRegion compRegion = subInterval.getCurrentSpace(); 566 if (LayoutRegion.overlap(compRegion, region, HORIZONTAL, 0) 567 && LayoutRegion.overlap(compRegion, region, VERTICAL, 0)) { 568 overlaps.add(component); 569 } 570 } 571 } 572 } 573 574 private static boolean[] overlappingGapDimensions(LayoutInterval layoutRoot, LayoutRegion region) { 576 boolean[] result = new boolean[2]; 577 int[][] overlapSides = overlappingGapSides(layoutRoot, region); 578 for (int i=0; i<DIM_COUNT; i++) { 579 result[i] = (overlapSides[i][0] == 1) && (overlapSides[i][1] == 1); 580 } 581 return result; 582 } 583 584 private static int[][] overlappingGapSides(LayoutInterval layoutRoot, LayoutRegion region) { 586 int[][] overlapSides = new int[][] {{0,0},{0,0}}; 587 List overlaps = new LinkedList(); 588 fillOverlappingComponents(overlaps, layoutRoot, region); 590 Iterator iter = overlaps.iterator(); 591 while (iter.hasNext()) { 592 LayoutComponent component = (LayoutComponent)iter.next(); 593 LayoutRegion compRegion = component.getLayoutInterval(HORIZONTAL).getCurrentSpace(); 594 for (int i=0; i<DIM_COUNT; i++) { 595 int[] edges = overlappingSides(compRegion, region, i); 596 for (int j=0; j<2; j++) { 597 if (edges[j] == 1) { 598 overlapSides[i][j] = 1; 599 } else if (edges[j] == -1) { 600 if (overlapSides[i][j] == -1) { 601 overlapSides[i][j] = 1; 602 } else if (overlapSides[i][j] == 0) { 603 overlapSides[i][j] = -1; 604 } 605 } 606 } 607 } 608 } 609 return overlapSides; 610 } 611 612 private static int[] overlappingSides(LayoutRegion compRegion, LayoutRegion region, int dimension) { 614 int[] sides = new int[2]; 615 int compLeading = compRegion.positions[dimension][LEADING]; 616 int compTrailing = compRegion.positions[dimension][TRAILING]; 617 int regLeading = region.positions[dimension][LEADING]; 618 int regTrailing = region.positions[dimension][TRAILING]; 619 if ((regLeading < compTrailing) && (compTrailing < regTrailing)) { 620 sides[0] = 1; 621 } 622 if ((regLeading < compLeading) && (compLeading < regTrailing)) { 623 sides[1] = 1; 624 } 625 if ((sides[0] == 1) && (sides[1] == 1)) { 626 sides[0] = sides[1] = -1; 627 } 628 return sides; 629 } 630 631 634 private boolean checkResizing() { 635 LayoutInterval interval = addingIntervals[dimension]; 636 int resizingEdge = dragger.getResizingEdge(dimension); 637 int fixedEdge = resizingEdge^1; 638 LayoutDragger.PositionDef newPos = newPositions[dimension]; 639 boolean resizing = false; 640 641 if (newPos != null && newPos.snapped && newPos.interval != null) { 642 int align1, align2; 643 if (newPos.interval.isParentOf(interval)) { 644 LayoutInterval parent = LayoutInterval.getFirstParent(interval, PARALLEL); 645 if (!LayoutRegion.pointInside(dragger.getMovingSpace(), resizingEdge, parent.getCurrentSpace(), dimension)) 646 parent = newPos.interval; 647 align1 = LayoutInterval.getEffectiveAlignmentInParent(interval, parent, fixedEdge); 648 align2 = resizingEdge; 649 } 650 else { 651 LayoutInterval parent = LayoutInterval.getCommonParent(interval, newPos.interval); 652 align1 = LayoutInterval.getEffectiveAlignmentInParent(interval, parent, fixedEdge); 653 align2 = newPos.nextTo ? 654 LayoutInterval.getEffectiveAlignmentInParent(newPos.interval, parent, newPos.alignment^1) : 655 resizingEdge; 656 } 657 if (align1 != align2 && (align1 == LEADING || align1 == TRAILING) && (align2 == LEADING || align2 == TRAILING)) 658 resizing = true; 659 } 660 return resizing; 664 685 } 686 687 691 private void addSimplyAligned() { 692 int alignment = aEdge; 693 assert alignment == CENTER || alignment == BASELINE; 694 layoutModel.setIntervalAlignment(addingInterval, alignment); 695 696 if (aSnappedParallel.isParallel() && aSnappedParallel.getGroupAlignment() == alignment) { 697 layoutModel.addInterval(addingInterval, aSnappedParallel, -1); 698 return; 699 } 700 LayoutInterval parent = aSnappedParallel.getParent(); 701 if (parent.isParallel() && parent.getGroupAlignment() == alignment) { 702 layoutModel.addInterval(addingInterval, parent, -1); 703 return; 704 } 705 706 int alignIndex = layoutModel.removeInterval(aSnappedParallel); 707 LayoutInterval subGroup = new LayoutInterval(PARALLEL); 708 subGroup.setGroupAlignment(alignment); 709 if (parent.isParallel()) { 710 subGroup.setAlignment(aSnappedParallel.getAlignment()); 711 } 712 layoutModel.setIntervalAlignment(aSnappedParallel, alignment); 713 layoutModel.addInterval(aSnappedParallel, subGroup, -1); 714 layoutModel.addInterval(addingInterval, subGroup, -1); 715 layoutModel.addInterval(subGroup, parent, alignIndex); 716 } 717 718 void addInterval(IncludeDesc iDesc1, IncludeDesc iDesc2) { 719 addToGroup(iDesc1, iDesc2, true); 720 721 if (iDesc1.snappedParallel != null || (iDesc2 != null && iDesc2.snappedParallel != null)) { 723 if (iDesc2 != null && iDesc2.snappedParallel != null) { 724 alignInParallel(addingInterval, iDesc2.snappedParallel, iDesc2.alignment); 725 } 726 if (iDesc1.snappedParallel != null) { 727 alignInParallel(addingInterval, iDesc1.snappedParallel, iDesc1.alignment); 728 } 729 } 730 checkParallelResizing(addingInterval, iDesc1, iDesc2); 731 732 LayoutInterval parent = addingInterval.getParent(); 734 int accAlign = DEFAULT; 735 if (parent.isSequential()) { 736 int tryAlign = parent.getAlignment() != TRAILING ? TRAILING : LEADING; 737 if (LayoutInterval.getDirectNeighbor(addingInterval, tryAlign, true) == null) { 738 accAlign = tryAlign; 739 } 740 else { 741 tryAlign ^= 1; 742 if (LayoutInterval.getDirectNeighbor(addingInterval, tryAlign, true) == null) 743 accAlign = tryAlign; 744 } 745 } 746 else { 747 accAlign = addingInterval.getAlignment() ^ 1; 748 } 749 if (accAlign != DEFAULT) { 750 accommodateOutPosition(addingInterval, accAlign); } 752 753 if (dragger.isResizing(dimension) && LayoutInterval.wantResize(addingInterval)) 754 operations.suppressResizingOfSurroundingGaps(addingInterval); 755 756 operations.optimizeGaps(LayoutInterval.getFirstParent(addingInterval, PARALLEL), dimension); 758 759 parent = addingInterval.getParent(); 760 if (parent.isSequential()) { int nonEmptyCount = LayoutInterval.getCount(parent, LayoutRegion.ALL_POINTS, true); 762 if (nonEmptyCount > 1 && dimension == HORIZONTAL) { 763 operations.moveInsideSequential(parent, dimension); 766 } 767 } 768 769 operations.mergeParallelGroups(LayoutInterval.getFirstParent(addingInterval, PARALLEL)); 771 } 772 773 private void addToGroup(IncludeDesc iDesc1, IncludeDesc iDesc2, boolean definite) { 774 assert iDesc2 == null || (iDesc1.parent == iDesc2.parent 775 && iDesc1.newSubGroup == iDesc2.newSubGroup 776 && iDesc1.neighbor == iDesc2.neighbor); 777 778 LayoutInterval parent = iDesc1.parent; 779 LayoutInterval seq = null; 780 int index = 0; 781 if (parent.isSequential()) { 782 if (iDesc1.newSubGroup) { 783 LayoutRegion space = addingSpace; 784 LayoutInterval subgroup = extractParallelSequence( 791 parent, space, false, iDesc1.alignment); if (subgroup != null) { seq = new LayoutInterval(SEQUENTIAL); 794 parent = subgroup; 795 } 796 } 797 if (seq == null) { 798 seq = parent; 799 parent = seq.getParent(); 800 index = iDesc1.index; 801 } 802 } 803 else { LayoutInterval neighbor = iDesc1.neighbor; 805 if (neighbor != null) { 806 assert neighbor.getParent() == parent; 807 seq = new LayoutInterval(SEQUENTIAL); 808 layoutModel.addInterval(seq, parent, layoutModel.removeInterval(neighbor)); 809 seq.setAlignment(neighbor.getAlignment()); 810 layoutModel.setIntervalAlignment(neighbor, DEFAULT); 811 layoutModel.addInterval(neighbor, seq, 0); 812 index = iDesc1.index; 813 } 814 else { 815 seq = new LayoutInterval(SEQUENTIAL); 816 seq.setAlignment(iDesc1.alignment); 817 } 818 } 819 820 assert iDesc1.alignment >= 0 || iDesc2 == null; 821 assert iDesc2 == null || iDesc2.alignment == (iDesc1.alignment^1); 822 assert parent.isParallel(); 823 824 LayoutInterval[] neighbors = new LayoutInterval[2]; LayoutInterval[] gaps = new LayoutInterval[2]; LayoutInterval originalGap = null; 827 int[] centerDst = new int[2]; 829 int count = seq.getSubIntervalCount(); 831 if (index > count) 832 index = count; 833 for (int i = LEADING; i <= TRAILING; i++) { 834 int idx1 = i == LEADING ? index - 1 : index; 835 int idx2 = i == LEADING ? index - 2 : index + 1; 836 if (idx1 >= 0 && idx1 < count) { 837 LayoutInterval li = seq.getSubInterval(idx1); 838 if (li.isEmptySpace()) { 839 originalGap = li; 840 if (idx2 >= 0 && idx2 < count) 841 neighbors[i] = seq.getSubInterval(idx2); 842 } 843 else neighbors[i] = li; 844 } 845 if (iDesc1.alignment < 0) { centerDst[i] = addingSpace.positions[dimension][CENTER] - 847 (neighbors[i] != null ? 848 getPerceivedNeighborPosition(neighbors[i], addingSpace, dimension, i^1) : 849 getPerceivedParentPosition(seq, parent, addingSpace, dimension, i)); 850 if (i == TRAILING) 851 centerDst[i] *= -1; 852 } 853 } 854 855 int edges = 2; 857 for (int i=LEADING; edges > 0; i^=1, edges--) { 858 gaps[i] = null; 859 LayoutInterval outerNeighbor = neighbors[i] == null ? 860 LayoutInterval.getNeighbor(parent, i, false, true, false) : null; 861 IncludeDesc iiDesc = iDesc1.alignment < 0 || iDesc1.alignment == i ? iDesc1 : iDesc2; 862 863 if (neighbors[i] == null && iiDesc != null) { if (iiDesc.snappedNextTo != null 865 && outerNeighbor != null && LayoutInterval.isDefaultPadding(outerNeighbor)) 866 { continue; 868 } 869 if (iiDesc.snappedParallel != null 870 && (!seq.isParentOf(iiDesc.snappedParallel) || originalGap == null)) 871 { continue; 873 } 874 } 875 876 boolean aligned; 877 if (iDesc1.alignment < 0) { aligned = centerDst[i] < centerDst[i^1] 879 || (centerDst[i] == centerDst[i^1] && i == LEADING); 880 } 881 else if (iDesc2 != null) { aligned = iiDesc.fixedPosition 883 || (i == LEADING && originalLPosFixed) 884 || (i == TRAILING && originalTPosFixed); 885 } 886 else { if (iDesc1.snappedParallel == null || !seq.isParentOf(iDesc1.snappedParallel)) 888 aligned = i == iDesc1.alignment; 889 else aligned = i == (iDesc1.alignment^1); 891 } 892 893 boolean minorGap = false; 894 if (!aligned && neighbors[i] == null && originalGap == null) { 895 IncludeDesc otherDesc = iiDesc == iDesc1 ? iDesc2 : iDesc1; 896 LayoutInterval parallel = otherDesc != null ? otherDesc.snappedParallel : null; 897 if (parallel == null && seq.getSubIntervalCount() == 0 && seq.getAlignment() != (i^1)) { 899 layoutModel.setIntervalAlignment(seq, i^1); 900 } 901 if (outerNeighbor != null && outerNeighbor.isEmptySpace()) { 902 continue; } 904 else { minorGap = (parallel != null && parallel.getParent() != null) 906 || (parent.getParent() != null && LayoutInterval.getCount(parent, i^1, true) > 0); 907 } 908 } 909 910 boolean fixedGap = aligned; 911 912 if (!fixedGap) { 913 if (minorGap || LayoutInterval.wantResize(addingInterval)) { 914 fixedGap = true; 915 } 916 else if (originalGap != null && !LayoutInterval.canResize(originalGap)) { 917 IncludeDesc otherDesc = iiDesc == iDesc1 ? iDesc2 : iDesc1; 918 if (otherDesc == null || otherDesc.snappedParallel == null 919 || neighbors[i] == null || LayoutInterval.getEffectiveAlignment(neighbors[i], i^1) == (i^1)) 920 fixedGap = true; 921 } 922 else if (originalGap == null) { 923 if ((neighbors[i] != null && LayoutInterval.getEffectiveAlignment(neighbors[i], i^1) == (i^1)) 924 || LayoutInterval.wantResize(seq)) 925 fixedGap = true; 927 } 928 } 929 930 LayoutInterval gap = new LayoutInterval(SINGLE); 931 if (!minorGap && (iiDesc == null || iiDesc.snappedNextTo == null)) { 932 LayoutRegion space = iiDesc != null && iiDesc.snappedParallel != null ? 934 iiDesc.snappedParallel.getCurrentSpace() : addingSpace; 935 int distance = neighbors[i] != null ? 936 LayoutRegion.distance(neighbors[i].getCurrentSpace(), space, dimension, i^1, i) : 937 LayoutRegion.distance(parent.getCurrentSpace(), space, dimension, i, i); 938 if (i == TRAILING) 939 distance *= -1; 940 941 if (distance > 0) { 942 int pad = neighbors[i] != null 943 || LayoutInterval.getNeighbor(parent, i, false, true, false) == null ? 944 determineExpectingPadding(addingInterval, neighbors[i], seq, i) : 945 Short.MIN_VALUE; if (distance > pad || (fixedGap && distance != pad)) { 947 gap.setPreferredSize(distance); 948 if (fixedGap) { 949 gap.setMinimumSize(USE_PREFERRED_SIZE); 950 gap.setMaximumSize(USE_PREFERRED_SIZE); 951 } 952 } 953 } 954 } 955 if (!fixedGap) { 956 gap.setMaximumSize(Short.MAX_VALUE); 957 IncludeDesc otherDesc = iiDesc == iDesc1 ? iDesc2 : iDesc1; 959 if (definite && neighbors[i] != null && parent.getParent() != null 960 && (otherDesc == null || otherDesc.alignment == DEFAULT) 961 && !isSignificantGroupEdge(seq, i^1)) 962 { parent = separateSequence(seq, i^1); 964 if (i == TRAILING) 965 edges++; } 967 } 968 969 gaps[i] = gap; 970 } 971 972 if (seq.getParent() == null) { assert seq.getSubIntervalCount() == 0; 974 if (gaps[LEADING] == null && gaps[TRAILING] == null) { layoutModel.setIntervalAlignment(addingInterval, seq.getAlignment()); 976 layoutModel.addInterval(addingInterval, parent, -1); 977 return; 978 } 979 else layoutModel.addInterval(seq, parent, -1); 980 } 981 982 if (iDesc1.snappedParallel != null && seq.isParentOf(iDesc1.snappedParallel)) { 985 iDesc1.snappedParallel = null; } 987 988 if (originalGap != null) { 990 index = layoutModel.removeInterval(originalGap); 991 } 992 else if (neighbors[TRAILING] != null) { 993 index = seq.indexOf(neighbors[TRAILING]); 994 } 995 else if (neighbors[LEADING] != null) { 996 index = seq.getSubIntervalCount(); 997 } 998 else index = 0; 999 1000 if (gaps[LEADING] != null) { 1001 layoutModel.addInterval(gaps[LEADING], seq, index++); 1002 } 1003 layoutModel.setIntervalAlignment(addingInterval, DEFAULT); 1004 layoutModel.addInterval(addingInterval, seq, index++); 1005 if (gaps[TRAILING] != null) { 1006 layoutModel.addInterval(gaps[TRAILING], seq, index); 1007 } 1008 } 1009 1010 private LayoutInterval extractParallelSequence(LayoutInterval seq, 1011 LayoutRegion space, 1012 boolean close, 1013 int alignment) 1014 { 1015 int count = seq.getSubIntervalCount(); 1016 int startIndex = 0; 1017 int endIndex = count - 1; 1018 int startPos = seq.getCurrentSpace().positions[dimension][LEADING]; 1019 int endPos = seq.getCurrentSpace().positions[dimension][TRAILING]; 1020 int point = alignment < 0 ? CENTER : alignment; 1021 1022 for (int i=0; i < count; i++) { 1023 LayoutInterval li = seq.getSubInterval(i); 1024 if (li.isEmptySpace()) 1025 continue; 1026 1027 LayoutRegion subSpace = li.getCurrentSpace(); 1028 boolean forcedParallel = !solveOverlap && LayoutUtils.contentOverlap(space, li, dimension); 1029 if (!forcedParallel && LayoutUtils.contentOverlap(space, li, dimension^1)) { if (getAddDirection(space, subSpace, dimension, point) == LEADING) { 1032 endIndex = i - 1; 1034 endPos = subSpace.positions[dimension][LEADING]; 1035 break; 1036 } 1037 else { startIndex = i + 1; 1039 startPos = subSpace.positions[dimension][TRAILING]; 1040 } 1041 } 1042 else if (close) { int[] detPos = space.positions[dimension]; 1044 int[] subPos = subSpace.positions[dimension]; 1045 if (detPos[LEADING] >= subPos[TRAILING]) { 1046 startIndex = i + 1; 1047 startPos = subPos[TRAILING]; 1048 } 1049 else if (detPos[LEADING] >= subPos[LEADING]) { 1050 startIndex = i; 1051 startPos = subPos[LEADING]; 1052 } 1053 else if (detPos[TRAILING] <= subPos[TRAILING]) { 1054 if (detPos[TRAILING] > subPos[LEADING]) { 1055 endIndex = i; 1056 endPos = subPos[TRAILING]; 1057 break; 1058 } 1059 else { endIndex = i - 1; 1061 endPos = subPos[LEADING]; 1062 break; 1063 } 1064 } 1065 } 1066 } 1067 1068 if (startIndex > endIndex) { 1069 return null; } 1071 if (startIndex == 0 && endIndex == count-1) { return seq.getParent(); 1073 } 1074 1075 LayoutInterval group = new LayoutInterval(PARALLEL); 1076 if (alignment != DEFAULT) { 1080 group.setGroupAlignment( alignment); 1082 } 1083 if (startIndex == endIndex) { 1084 LayoutInterval li = layoutModel.removeInterval(seq, startIndex); 1085 layoutModel.addInterval(li, group, 0); 1086 } 1087 else { 1088 LayoutInterval interSeq = new LayoutInterval(SEQUENTIAL); 1089 group.add(interSeq, 0); 1090 int i = startIndex; 1091 while (i <= endIndex) { 1092 LayoutInterval li = layoutModel.removeInterval(seq, i); 1093 endIndex--; 1094 layoutModel.addInterval(li, interSeq, -1); 1095 } 1096 } 1097 layoutModel.addInterval(group, seq, startIndex); 1098 1099 group.getCurrentSpace().set(dimension, startPos, endPos); 1100 1101 return group; 1102 } 1103 1104 private static int getPerceivedParentPosition(LayoutInterval interval, LayoutInterval parent, 1105 LayoutRegion space, int dimension, int alignment) 1106 { 1107 int position = Integer.MIN_VALUE; 1108 do { 1109 if (parent.isSequential()) { 1110 interval = parent; 1111 parent = interval.getParent(); 1112 } 1113 1114 LayoutInterval neighbor = null; 1115 while (neighbor == null && parent.getParent() != null) { 1116 boolean significantEdge = interval.getParent() != null ? 1117 isSignificantGroupEdge(interval, alignment) : 1118 LayoutInterval.isClosedGroup(parent, alignment); 1119 if (significantEdge) 1120 break; 1121 1122 neighbor = LayoutInterval.getDirectNeighbor(parent, alignment, true); 1123 if (neighbor == null) { 1124 interval = parent; 1125 parent = interval.getParent(); 1126 if (parent.isSequential()) { 1127 interval = parent; 1128 parent = interval.getParent(); 1129 } 1130 } 1131 } 1132 1133 if (neighbor == null) { 1134 position = parent.getCurrentSpace().positions[dimension][alignment]; 1135 } 1136 else { do { 1138 position = getPerceivedNeighborPosition(neighbor, space, dimension, alignment^1); 1139 if (position != Integer.MIN_VALUE) 1140 break; 1141 else neighbor = LayoutInterval.getDirectNeighbor(neighbor, alignment, true); 1143 } 1144 while (neighbor != null); 1145 if (neighbor == null) { 1146 interval = parent; 1147 parent = interval.getParent(); 1148 } 1149 } 1150 } 1151 while (position == Integer.MIN_VALUE); 1152 return position; 1153 } 1154 1155 private static boolean isSignificantGroupEdge(LayoutInterval interval, int alignment) { 1156 LayoutInterval group = interval.getParent(); 1157 assert group.isParallel(); 1158 if (interval.getAlignment() == alignment || LayoutInterval.wantResize(interval)) 1159 return true; 1160 1161 if (!LayoutInterval.isClosedGroup(group, alignment)) 1162 return false; 1163 1164 if (!LayoutInterval.isExplicitlyClosedGroup(group)) { LayoutInterval neighborGap = LayoutInterval.getNeighbor(group, alignment, false, true, true); 1166 if (neighborGap != null && LayoutInterval.isDefaultPadding(neighborGap)) { 1167 return false; 1169 } 1170 } 1171 return true; 1172 } 1173 1174 private static int getPerceivedNeighborPosition(LayoutInterval neighbor, LayoutRegion space, int dimension, int alignment) { 1175 assert !neighbor.isEmptySpace(); 1176 1177 if (neighbor.isComponent()) 1178 return neighbor.getCurrentSpace().positions[dimension][alignment]; 1181 1182 int neighborPos = Integer.MIN_VALUE; 1183 int n = neighbor.getSubIntervalCount(); 1184 int i, d; 1185 if (neighbor.isParallel() || alignment == LEADING) { 1186 d = 1; 1187 i = 0; 1188 } 1189 else { 1190 d = -1; 1191 i = n - 1; 1192 } 1193 1194 while (i >=0 && i < n) { 1195 LayoutInterval sub = neighbor.getSubInterval(i); 1196 i += d; 1197 1198 if (sub.isEmptySpace() 1199 || (sub.isComponent() 1200 && !LayoutRegion.overlap(space, sub.getCurrentSpace(), dimension^1, 0))) 1201 continue; 1202 1203 int pos = getPerceivedNeighborPosition(sub, space, dimension, alignment); 1204 if (pos != Integer.MIN_VALUE) { 1205 if (neighbor.isSequential()) { 1206 neighborPos = pos; 1207 break; 1208 } 1209 else if (neighborPos == Integer.MIN_VALUE || pos*d < neighborPos*d) { 1210 neighborPos = pos; 1211 } 1213 } 1214 } 1215 return neighborPos; 1216 } 1217 1218 private LayoutInterval separateSequence(LayoutInterval seq, int alignment) { 1219 LayoutInterval parentPar = seq.getParent(); 1220 assert parentPar.isParallel(); 1221 while (!parentPar.getParent().isSequential()) { 1222 parentPar = parentPar.getParent(); 1223 } 1224 LayoutInterval parentSeq = parentPar.getParent(); 1226 int d = alignment == LEADING ? -1 : 1; 1227 int n = parentSeq.getSubIntervalCount(); 1228 int end = parentSeq.indexOf(parentPar) + d; 1229 while (end >= 0 && end < n) { 1230 LayoutInterval sub = parentSeq.getSubInterval(end); 1231 if (!sub.isEmptySpace()) { 1232 if (LayoutUtils.contentOverlap(addingSpace, sub, dimension^1)) { 1237 break; 1238 } 1239 } 1240 end += d; 1241 } 1242 1243 int endPos = end >= 0 && end < n ? 1244 parentSeq.getSubInterval(end).getCurrentSpace().positions[dimension][alignment^1] : 1245 parentSeq.getParent().getCurrentSpace().positions[dimension][alignment]; 1246 end -= d; 1247 operations.parallelizeWithParentSequence(seq, end, dimension); 1248 parentPar = seq.getParent(); 1249 parentPar.getCurrentSpace().positions[dimension][alignment] = endPos; 1250 return parentPar; 1251 } 1252 1253 1260 private void accommodateOutPosition(LayoutInterval interval, int alignment) { 1261 if (alignment == CENTER || alignment == BASELINE) { 1262 return; } 1264 1265 int pos = interval.getCurrentSpace().positions[dimension][alignment]; 1266 assert pos != LayoutRegion.UNKNOWN; 1267 int sizeIncrement = Integer.MIN_VALUE; 1268 int d = alignment == LEADING ? -1 : 1; 1269 int[] groupPos = null; 1270 LayoutInterval parent = interval.getParent(); 1271 LayoutInterval prev = null; 1272 1273 do { 1274 if (parent.isSequential()) { 1275 if (sizeIncrement > 0) { 1276 int accommodated = accommodateSizeInSequence(interval, prev, sizeIncrement, alignment); 1277 sizeIncrement -= accommodated; 1278 if (groupPos != null) { 1279 groupPos[alignment] += accommodated * d; 1280 } 1281 } 1282 LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(interval, alignment, false); 1283 if (neighbor != null && (!neighbor.isEmptySpace() || LayoutInterval.canResize(neighbor))) { 1284 return; 1286 } 1287 prev = interval; 1288 } 1289 else { 1290 groupPos = parent.getCurrentSpace().positions[dimension]; 1291 if (groupPos[alignment] != LayoutRegion.UNKNOWN) { 1292 sizeIncrement = (pos - groupPos[alignment]) * d; 1293 if (sizeIncrement > 0) { 1294 int subPos[] = interval.getCurrentSpace().positions[dimension]; 1295 if (!interval.getCurrentSpace().isSet(dimension) 1296 || subPos[alignment]*d < groupPos[alignment]*d) 1297 { subPos[alignment] = groupPos[alignment]; 1299 } 1300 } 1301 } 1302 else groupPos = null; 1303 if (!interval.isSequential()) 1304 prev = interval; 1305 } 1306 interval = parent; 1307 parent = interval.getParent(); 1308 } 1309 while ((sizeIncrement > 0 || sizeIncrement == Integer.MIN_VALUE) 1310 && parent != null 1311 && (!parent.isParallel() || interval.getAlignment() != alignment)); 1312 } 1314 1315 private int accommodateSizeInSequence(LayoutInterval interval, LayoutInterval lower, int sizeIncrement, int alignment) { 1316 LayoutInterval parent = interval.getParent(); 1317 assert parent.isSequential(); 1318 LayoutRegion space = lower.getCurrentSpace(); 1319 int increment = sizeIncrement; 1320 int pos = interval.getCurrentSpace().positions[dimension][alignment]; 1321 int outPos = parent.getParent().getCurrentSpace().positions[dimension][alignment]; 1322 1323 boolean parallel = false; 1324 int d = alignment == LEADING ? -1 : 1; 1325 int start = parent.indexOf(interval); 1326 int end = lower.isComponent() ? start : -1; 1327 int n = parent.getSubIntervalCount(); 1328 1329 for (int i=start+d; i >= 0 && i < n; i+=d) { 1330 LayoutInterval li = parent.getSubInterval(i); 1331 if (end != -1) { int endPos = Integer.MIN_VALUE; 1334 if (!li.isEmptySpace()) { 1335 if (LayoutUtils.contentOverlap(space, li, dimension^1)) { 1336 if (end != start) { 1337 end = i - d; 1338 endPos = li.getCurrentSpace().positions[dimension][alignment^1]; 1339 } 1340 else end = -1; } 1342 else { end = i; 1344 if (!parallel && LayoutUtils.contentOverlap(space, li, dimension)) 1345 parallel = true; 1346 } 1347 } 1348 if ((i == 0 || i+d == n) && endPos == Integer.MIN_VALUE && end != -1) { if (end != start && (parallel || dimension == HORIZONTAL)) { 1350 end = i; 1351 endPos = outPos; 1352 } 1353 else end = -1; } 1355 if (endPos != Integer.MIN_VALUE) { 1356 LayoutInterval toPar = lower.getParent().isSequential() ? lower.getParent() : lower; 1357 LayoutInterval endGap = LayoutInterval.getDirectNeighbor(lower, alignment, false); 1358 if (endGap == null && !LayoutInterval.isAlignedAtBorder(toPar, alignment)) { 1359 endGap = new LayoutInterval(SINGLE); 1360 if (!toPar.isSequential()) { 1361 toPar = new LayoutInterval(SEQUENTIAL); 1362 layoutModel.addInterval(toPar, lower.getParent(), layoutModel.removeInterval(lower)); 1363 layoutModel.setIntervalAlignment(toPar, lower.getRawAlignment()); 1364 layoutModel.setIntervalAlignment(lower, DEFAULT); 1365 layoutModel.addInterval(lower, toPar, 0); 1366 } 1367 layoutModel.addInterval(endGap, toPar, alignment==LEADING ? 0 : -1); 1368 } 1369 else assert endGap == null || endGap.isEmptySpace(); 1370 1371 operations.parallelizeWithParentSequence(toPar, end, dimension); 1372 1373 if (alignment == TRAILING) i -= n - parent.getSubIntervalCount(); 1375 n = parent.getSubIntervalCount(); end = -1; 1378 increment -= Math.abs(endPos - pos); 1379 if (increment < 0) 1380 increment = 0; 1381 continue; 1382 } 1383 else if (end == -1) { i = start; continue; 1386 } 1387 } 1388 else if (li.isEmptySpace() && li.getPreferredSize() != NOT_EXPLICITLY_DEFINED) { 1390 int pad = determinePadding(interval, dimension, alignment); 1391 int currentSize = LayoutInterval.getIntervalCurrentSize(li, dimension); 1392 1393 int size = currentSize - increment; 1394 if (size <= pad) { 1395 size = NOT_EXPLICITLY_DEFINED; 1396 increment -= currentSize - pad; 1397 } 1398 else increment = 0; 1399 1400 operations.resizeInterval(li, size); 1401 if (LayoutInterval.wantResize(li) && LayoutInterval.wantResize(interval)) { 1402 layoutModel.setIntervalSize(li, li.getMinimumSize(), li.getPreferredSize(), USE_PREFERRED_SIZE); 1404 } 1405 break; 1406 } 1407 else { 1408 interval = li; 1409 } 1410 } 1411 return sizeIncrement - increment; 1412 } 1413 1414 1421 private LayoutInterval alignInParallel(LayoutInterval interval, LayoutInterval toAlignWith, int alignment) { 1422 assert alignment == LEADING || alignment == TRAILING; 1423 1424 if (toAlignWith.isParentOf(interval) || interval.isParentOf(toAlignWith)) { return null; 1428 } 1429 else { 1430 LayoutInterval commonParent = LayoutInterval.getCommonParent(interval, toAlignWith); 1431 if (commonParent == null || commonParent.isSequential()) return null; 1433 } 1434 1435 boolean resizing = LayoutInterval.wantResize(interval); 1437 LayoutInterval aligning = interval; LayoutInterval parParent = LayoutInterval.getFirstParent(interval, PARALLEL); 1439 while (!parParent.isParentOf(toAlignWith)) { 1440 if (LayoutInterval.isAlignedAtBorder(aligning, parParent, alignment)) { if (resizing && !LayoutInterval.canResize(parParent)) 1446 operations.enableGroupResizing(parParent); 1447 aligning = parParent; 1448 parParent = LayoutInterval.getFirstParent(aligning, PARALLEL); 1449 } 1450 else parParent = null; 1451 if (parParent == null) return null; } 1454 1455 boolean resizingOp = dragger.isResizing(dimension); 1456 1457 LayoutInterval tempRemoved = aligning; 1459 while (tempRemoved.getParent() != parParent) 1460 tempRemoved = tempRemoved.getParent(); 1461 int removedIndex = parParent.remove(tempRemoved); 1462 1463 boolean alignWithParent = false; 1465 LayoutInterval alignParent; 1466 do { 1467 alignParent = LayoutInterval.getFirstParent(toAlignWith, PARALLEL); 1468 if (alignParent == null) 1469 return null; if (canSubstAlignWithParent(toAlignWith, dimension, alignment, resizingOp)) { 1471 if (alignParent == parParent) { 1473 alignWithParent = true; 1475 } 1476 else toAlignWith = alignParent; 1477 } 1478 } 1479 while (toAlignWith == alignParent); 1480 1481 parParent.add(tempRemoved, removedIndex); 1483 if (alignParent != parParent) 1484 return null; 1486 if (aligning != interval) { 1487 if (!LayoutInterval.isAlignedAtBorder(toAlignWith, alignment)) { 1488 int dst = LayoutRegion.distance(aligning.getCurrentSpace(), 1490 toAlignWith.getCurrentSpace(), 1491 dimension, alignment, alignment) 1492 * (alignment == TRAILING ? -1 : 1); 1493 if (dst > 0) { tempRemoved = interval; 1496 while (tempRemoved.getParent() != aligning) 1497 tempRemoved = tempRemoved.getParent(); 1498 removedIndex = aligning.remove(tempRemoved); 1499 1500 operations.cutStartingGap(aligning, dst, dimension, alignment); 1501 1502 aligning.add(tempRemoved, removedIndex); } 1504 } 1505 optimizeStructure = true; 1506 } 1507 1508 int effAlign1 = LayoutInterval.getEffectiveAlignment(toAlignWith, alignment); 1510 1522 List alignedList = new ArrayList(2); 1524 List remainder = new ArrayList(2); 1525 int originalCount = parParent.getSubIntervalCount(); 1526 1527 int extAlign1 = extract(toAlignWith, alignedList, remainder, alignment); 1528 extract(aligning, alignedList, remainder, alignment); 1529 1530 assert !alignWithParent || remainder.isEmpty(); 1531 1532 int indent = LayoutRegion.distance(toAlignWith.getCurrentSpace(), interval.getCurrentSpace(), 1534 dimension, alignment, alignment); 1535 assert indent == 0 || alignment == LEADING; if (indent != 0) { 1537 LayoutInterval indentGap = new LayoutInterval(SINGLE); 1538 indentGap.setSize(Math.abs(indent)); 1539 LayoutInterval parent = interval.getParent(); 1541 if (parent == null || !parent.isSequential()) { 1542 LayoutInterval seq = new LayoutInterval(SEQUENTIAL); 1543 if (parent != null) { 1544 layoutModel.addInterval(seq, parent, layoutModel.removeInterval(interval)); 1545 } 1546 layoutModel.setIntervalAlignment(interval, DEFAULT); 1547 layoutModel.addInterval(interval, seq, 0); 1548 parent = seq; 1549 } 1550 layoutModel.addInterval(indentGap, parent, alignment == LEADING ? 0 : -1); 1551 if (interval == aligning) 1552 alignedList.set(alignedList.size()-1, parent); 1553 } 1554 1555 LayoutInterval group; 1557 LayoutInterval commonSeq; 1558 if (alignWithParent || (originalCount == 2 && parParent.getParent() != null)) { 1559 group = parParent; 1561 if (!remainder.isEmpty()) { LayoutInterval groupParent = group.getParent(); 1563 if (groupParent.isSequential()) { 1564 commonSeq = groupParent; 1565 } 1566 else { int index = layoutModel.removeInterval(group); 1568 commonSeq = new LayoutInterval(SEQUENTIAL); 1569 commonSeq.setAlignment(group.getAlignment()); 1570 layoutModel.addInterval(commonSeq, groupParent, index); 1571 layoutModel.setIntervalAlignment(group, DEFAULT); 1573 layoutModel.addInterval(group, commonSeq, -1); 1574 } 1575 } 1576 else commonSeq = null; 1577 } 1578 else { group = new LayoutInterval(PARALLEL); 1580 group.setGroupAlignment(alignment); 1581 if (!remainder.isEmpty()) { commonSeq = new LayoutInterval(SEQUENTIAL); 1583 commonSeq.add(group, 0); 1584 if (effAlign1 == LEADING || effAlign1 == TRAILING) { 1585 commonSeq.setAlignment(effAlign1); 1586 } 1587 layoutModel.addInterval(commonSeq, parParent, -1); 1588 } 1590 else { 1591 commonSeq = null; 1592 if (effAlign1 == LEADING || effAlign1 == TRAILING) { 1593 group.setAlignment(effAlign1); 1594 } 1595 layoutModel.addInterval(group, parParent, -1); 1596 } 1597 if (alignment == LEADING || alignment == TRAILING) { 1598 int alignPos = toAlignWith.getCurrentSpace().positions[dimension][alignment]; 1599 int outerPos = parParent.getCurrentSpace().positions[dimension][alignment^1]; 1600 group.getCurrentSpace().set(dimension, 1601 alignment == LEADING ? alignPos : outerPos, 1602 alignment == LEADING ? outerPos : alignPos); 1603 } 1604 } 1605 1606 LayoutInterval aligning2 = (LayoutInterval) alignedList.get(1); 1608 if (aligning2.getParent() != group) { 1609 if (aligning2.getParent() != null) { 1610 layoutModel.removeInterval(aligning2); 1611 } 1612 layoutModel.addInterval(aligning2, group, -1); 1613 } 1614 if (!LayoutInterval.isAlignedAtBorder(aligning2, alignment)) { 1615 layoutModel.setIntervalAlignment(aligning2, alignment); 1616 } 1617 1618 LayoutInterval aligning1 = (LayoutInterval) alignedList.get(0); 1619 if (aligning1.getParent() != group) { 1620 if (aligning1.getParent() != null) { 1621 layoutModel.setIntervalAlignment(aligning1, extAlign1); layoutModel.removeInterval(aligning1); 1623 } 1624 layoutModel.addInterval(aligning1, group, -1); 1625 } 1626 if (!resizingOp && group.getSubIntervalCount() == 2 1627 && !LayoutInterval.isAlignedAtBorder(aligning1, alignment) 1628 && !LayoutInterval.isAlignedAtBorder(aligning2, alignment^1)) { 1629 layoutModel.setIntervalAlignment(aligning1, alignment); 1630 } 1631 1632 if (!remainder.isEmpty()) { 1634 int index = commonSeq.indexOf(group); 1635 if (alignment == TRAILING) 1636 index++; 1637 LayoutInterval sideGroup = operations.addGroupContent( 1638 remainder, commonSeq, index, dimension, alignment); 1639 if (sideGroup != null) { 1640 int pos1 = parParent.getCurrentSpace().positions[dimension][alignment]; 1641 int pos2 = toAlignWith.getCurrentSpace().positions[dimension][alignment]; 1642 sideGroup.getCurrentSpace().set(dimension, 1643 alignment == LEADING ? pos1 : pos2, 1644 alignment == LEADING ? pos2 : pos1); 1645 operations.optimizeGaps(sideGroup, dimension); 1646 operations.mergeParallelGroups(sideGroup); 1647 } 1648 } 1649 1650 return group; 1651 } 1652 1653 private int extract(LayoutInterval interval, List toAlign, List toRemain, int alignment) { 1654 int effAlign = LayoutInterval.getEffectiveAlignment(interval, alignment); 1655 LayoutInterval parent = interval.getParent(); 1656 if (parent.isSequential()) { 1657 int extractCount = operations.extract(interval, alignment, false, 1658 alignment == LEADING ? toRemain : null, 1659 alignment == LEADING ? null : toRemain); 1660 if (extractCount == 1) { if (effAlign == LEADING || effAlign == TRAILING) 1662 layoutModel.setIntervalAlignment(interval, effAlign); 1663 layoutModel.removeInterval(parent); 1664 toAlign.add(interval); 1665 } 1666 else { toAlign.add(parent); 1668 } 1669 } 1670 else { 1671 toAlign.add(interval); 1672 } 1673 return effAlign; 1674 } 1675 1676 private void checkParallelResizing(LayoutInterval interval, IncludeDesc iDesc1, IncludeDesc iDesc2) { 1677 LayoutInterval parallelInt; 1678 LayoutInterval group = interval.getParent(); 1679 if (group.isSequential()) { 1680 parallelInt = group; 1681 group = group.getParent(); 1682 } 1683 else parallelInt = interval; 1684 1685 if (group.getParent() == null) 1687 return; 1688 int rootAlign = DEFAULT; 1690 if (iDesc1.snappedNextTo != null && iDesc1.snappedNextTo.getParent() == null) 1691 rootAlign = iDesc1.alignment; 1692 if (iDesc2 != null && iDesc2.snappedNextTo != null && iDesc2.snappedNextTo.getParent() == null) 1693 rootAlign = rootAlign == DEFAULT ? iDesc2.alignment : LayoutRegion.ALL_POINTS; 1694 if (rootAlign == LEADING || rootAlign == TRAILING) { 1695 int remIdx = group.remove(parallelInt); LayoutInterval neighbor = LayoutInterval.getNeighbor(group, rootAlign^1, false, true, true); 1698 if ((neighbor != null 1699 && neighbor.getPreferredSize() == NOT_EXPLICITLY_DEFINED 1700 && LayoutInterval.isAlignedAtBorder(neighbor.getParent(), LayoutInterval.getRoot(neighbor), rootAlign^1)) 1701 || 1702 (neighbor == null 1703 && LayoutInterval.isAlignedAtBorder(group, LayoutInterval.getRoot(group), rootAlign^1))) 1704 { rootAlign = LayoutRegion.ALL_POINTS; 1706 } 1707 group.add(parallelInt, remIdx); 1708 } 1709 if (rootAlign == LayoutRegion.ALL_POINTS) 1710 return; 1711 1712 LayoutInterval neighborGap = null; 1714 if (interval != parallelInt) { 1715 assert parallelInt.isSequential(); 1716 for (int i=LEADING; i <= TRAILING; i++) { 1717 LayoutInterval gap = LayoutInterval.getDirectNeighbor(interval, i, false); 1718 if (gap != null && gap.isEmptySpace() && LayoutInterval.canResize(gap)) { 1719 neighborGap = gap; 1720 break; 1721 } 1722 } 1723 } 1724 1725 if (LayoutInterval.wantResize(interval)) { 1727 if (!dragger.isResizing(dimension)) 1728 return; 1729 } 1730 else if (neighborGap == null) 1731 return; 1732 1733 if (!LayoutInterval.canResize(group) 1734 && ((iDesc1.snappedNextTo != null && !group.isParentOf(iDesc1.snappedNextTo)) 1735 || (iDesc2 != null && iDesc2.snappedNextTo != null && !group.isParentOf(iDesc2.snappedNextTo)))) 1736 { operations.enableGroupResizing(group); 1738 } 1739 1740 if (LayoutInterval.canResize(group) && group.getParent() != null) { 1741 boolean contentResizing = false; 1743 boolean samePosition = false; 1744 for (Iterator it=group.getSubIntervals(); it.hasNext(); ) { 1745 LayoutInterval li = (LayoutInterval) it.next(); 1746 if (li != parallelInt) { 1747 if (LayoutInterval.wantResize(li)) { 1748 contentResizing = true; 1749 break; 1750 } 1751 if (!samePosition) { 1752 int align = li.getAlignment(); 1753 if (align == LEADING || align == TRAILING) 1754 samePosition = getExpectedBorderPosition(parallelInt, dimension, align^1) 1755 == getExpectedBorderPosition(li, dimension, align^1); 1756 } 1757 } 1758 } 1759 if (!contentResizing && samePosition) { 1760 operations.suppressGroupResizing(group); 1761 } 1762 } 1763 1764 if (!LayoutInterval.canResize(group)) { 1765 layoutModel.changeIntervalAttribute(parallelInt, LayoutInterval.ATTRIBUTE_FILL, true); 1767 if (neighborGap != null) { 1768 layoutModel.setIntervalSize(neighborGap, NOT_EXPLICITLY_DEFINED, NOT_EXPLICITLY_DEFINED, Short.MAX_VALUE); 1769 } 1770 else if (interval.isComponent()) { 1771 java.awt.Dimension sizeLimit; 1772 LayoutComponent lc = interval.getComponent(); 1773 sizeLimit = lc.isLayoutContainer() ? 1774 operations.getMapper().getComponentMinimumSize(lc.getId()) : 1775 operations.getMapper().getComponentPreferredSize(lc.getId()); 1776 int pref = dimension == HORIZONTAL ? sizeLimit.width : sizeLimit.height; 1777 if (interval.getPreferredSize() < pref) 1778 layoutModel.setIntervalSize(interval, 0, 0, interval.getMaximumSize()); 1779 else 1780 layoutModel.setIntervalSize(interval, 1781 interval.getMinimumSize() != USE_PREFERRED_SIZE ? interval.getMinimumSize() : NOT_EXPLICITLY_DEFINED, 1782 NOT_EXPLICITLY_DEFINED, 1783 interval.getMaximumSize()); 1784 } 1785 } 1786 1787 if (interval.isComponent() && neighborGap == null 1788 && (parallelInt == interval || LayoutInterval.getCount(parallelInt, DEFAULT, true) == 1)) 1789 { setParallelSameSize(group, parallelInt, dimension); 1791 } 1792 } 1793 1794 private void setParallelSameSize(LayoutInterval group, LayoutInterval aligned, int dimension) { 1795 LayoutInterval alignedComp = getOneNonEmpty(aligned); 1796 1797 for (Iterator it=group.getSubIntervals(); it.hasNext(); ) { 1798 LayoutInterval li = (LayoutInterval) it.next(); 1799 if (li != aligned) { 1800 if (li.isParallel()) { 1801 setParallelSameSize(li, alignedComp, dimension); 1802 } 1803 else { 1804 LayoutInterval sub = getOneNonEmpty(li); 1805 if (sub != null 1806 && LayoutRegion.sameSpace(alignedComp.getCurrentSpace(), sub.getCurrentSpace(), dimension) 1807 && !LayoutInterval.wantResize(li)) 1808 { if (sub.isParallel()) { 1810 setParallelSameSize(sub, alignedComp, dimension); 1811 } 1812 else { layoutModel.setIntervalAlignment(li, aligned.getAlignment()); 1814 int min = sub.getMinimumSize(); 1815 layoutModel.setIntervalSize(sub, 1816 min != USE_PREFERRED_SIZE ? min : NOT_EXPLICITLY_DEFINED, 1817 sub.getPreferredSize(), 1818 Short.MAX_VALUE); 1819 layoutModel.changeIntervalAttribute(sub, LayoutInterval.ATTRIBUTE_FILL, true); 1820 } 1821 } 1822 } 1823 } 1824 } 1825 } 1826 1827 private static LayoutInterval getOneNonEmpty(LayoutInterval interval) { 1828 if (!interval.isSequential()) 1829 return interval; 1830 1831 LayoutInterval nonEmpty = null; 1832 for (Iterator it=interval.getSubIntervals(); it.hasNext(); ) { 1833 LayoutInterval li = (LayoutInterval) it.next(); 1834 if (!li.isEmptySpace()) { 1835 if (nonEmpty == null) 1836 nonEmpty = li; 1837 else 1838 return null; 1839 } 1840 } 1841 return nonEmpty; 1842 } 1843 1844 private int getExpectedBorderPosition(LayoutInterval interval, int dimension, int alignment) { 1845 LayoutInterval comp = LayoutUtils.getOutermostComponent(interval, dimension, alignment); 1846 int pos = comp.getCurrentSpace().positions[dimension][alignment]; 1847 LayoutInterval neighbor = LayoutInterval.getNeighbor(comp, alignment, false, true, false); 1848 if (neighbor != null && neighbor.isEmptySpace() && interval.isParentOf(neighbor)) { 1849 int diff = neighbor.getPreferredSize(); 1850 if (diff == NOT_EXPLICITLY_DEFINED) 1851 diff = LayoutUtils.getSizeOfDefaultGap(neighbor, operations.getMapper()); 1852 if (alignment == LEADING) 1853 diff *= -1; 1854 pos += diff; 1855 } 1856 return pos; 1857 } 1858 1859 private int determinePadding(LayoutInterval interval, int dimension, int alignment) { 1860 LayoutInterval neighbor = LayoutInterval.getNeighbor(interval, alignment, true, true, false); 1861 return dragger.findPadding(neighbor, interval, dimension, alignment); 1862 } 1864 1865 1870 private int determineExpectingPadding(LayoutInterval addingInt, 1871 LayoutInterval baseInt, 1872 LayoutInterval baseParent, 1873 int alignment) 1874 { 1875 if (baseInt == null) { 1876 baseInt = LayoutInterval.getNeighbor(baseParent, SEQUENTIAL, alignment); 1877 } 1878 return dragger.findPadding(baseInt, addingInt, dimension, alignment); 1879 } 1880 1881 1883 private void analyzeParallel(LayoutInterval group, List inclusions) { 1884 Iterator it = group.getSubIntervals(); 1885 while (it.hasNext()) { 1886 LayoutInterval sub = (LayoutInterval) it.next(); 1887 if (sub.isEmptySpace()) 1888 continue; 1889 1890 LayoutRegion subSpace = sub.getCurrentSpace(); 1891 1892 if (sub.isParallel() 1893 && pointInside(addingSpace, aEdge, sub, dimension) 1894 && shouldEnterGroup(sub)) 1895 { analyzeParallel(sub, inclusions); 1897 } 1898 else if (sub.isSequential()) { 1899 analyzeSequential(sub, inclusions); 1902 } 1903 else if (orthogonalOverlap(sub)) { 1904 boolean dimOverlap = LayoutRegion.overlap(addingSpace, subSpace, dimension, 0); 1905 if (dimOverlap && !solveOverlap) { 1906 IncludeDesc origPos = originalPositions1[dimension]; 1907 if (origPos == null || (aEdge^1) != origPos.alignment 1908 || (origPos.parent != sub && !sub.isParentOf(origPos.parent))) 1909 { continue; 1911 } 1912 } 1916 if (dimOverlap) 1917 imposeSize = true; 1918 1919 int distance = LayoutRegion.UNKNOWN; 1920 if (aSnappedNextTo != null) { 1921 LayoutInterval neighbor; 1923 if (sub == aSnappedNextTo 1924 || sub.isParentOf(aSnappedNextTo) 1925 || aSnappedNextTo.getParent() == null 1926 || (neighbor = LayoutInterval.getNeighbor(sub, aEdge, true, true, false)) == aSnappedNextTo 1927 || (neighbor != null && neighbor.isParentOf(aSnappedNextTo))) 1928 { distance = -1; } 1931 } 1932 if (distance != -1) { 1933 if (!dimOverlap) { int dstL = LayoutRegion.distance(subSpace, addingSpace, 1935 dimension, TRAILING, LEADING); 1936 int dstT = LayoutRegion.distance(addingSpace, subSpace, 1937 dimension, TRAILING, LEADING); 1938 distance = dstL >= 0 ? dstL : dstT; 1939 } 1940 else distance = 0; } 1942 1943 IncludeDesc iDesc = addInclusion(group, false, distance, 0, inclusions); 1944 if (iDesc != null) { 1945 iDesc.neighbor = sub; 1946 int point = aEdge < 0 ? CENTER : aEdge; 1947 iDesc.index = getAddDirection(addingSpace, subSpace, dimension, point) == LEADING ? 0 : 1; 1948 } 1949 } 1950 } 1951 1952 if (inclusions.isEmpty()) { if (group.getParent() == null 1954 && (aSnappedParallel == null || canAlignWith(aSnappedParallel, group, aEdge))) 1955 { int distance = aSnappedNextTo == group ? -1 : Integer.MAX_VALUE; 1957 addInclusion(group, false, distance, Integer.MAX_VALUE, inclusions); 1958 } 1959 } 1960 } 1961 1962 private void analyzeSequential(LayoutInterval group, List inclusions) { 1963 boolean inSequence = false; 1964 boolean parallelWithSequence = false; 1965 int index = -1; 1966 int distance = Integer.MAX_VALUE; 1967 int ortDistance = Integer.MAX_VALUE; 1968 1969 for (int i=0,n=group.getSubIntervalCount(); i < n; i++) { 1970 LayoutInterval sub = group.getSubInterval(i); 1971 if (sub.isEmptySpace()) { 1972 if (index == i) 1973 index++; 1974 continue; 1975 } 1976 1977 LayoutRegion subSpace = sub.getCurrentSpace(); 1978 1979 if (sub.isParallel() 1981 && pointInside(addingSpace, aEdge, sub, dimension) 1982 && shouldEnterGroup(sub)) 1983 { int count = inclusions.size(); 1985 analyzeParallel(sub, inclusions); 1986 if (inclusions.size() > count) 1987 return; 1988 } 1989 1990 boolean ortOverlap = orthogonalOverlap(sub); 1992 int margin = (dimension == VERTICAL && !ortOverlap ? 4 : 0); 1993 boolean dimOverlap = LayoutRegion.overlap(addingSpace, subSpace, dimension, margin); 1994 if (ortOverlap || (dimension == VERTICAL && !dimOverlap && !parallelWithSequence)) { 1997 if (dimOverlap) { if (!solveOverlap) { IncludeDesc origPos = originalPositions1[dimension]; 2000 if (origPos == null || (aEdge^1) != origPos.alignment 2001 || (origPos.parent != sub && !sub.isParentOf(origPos.parent))) 2002 { return; 2004 } 2005 } 2009 if (ortOverlap) 2010 imposeSize = true; 2011 2012 inSequence = true; 2013 distance = ortDistance = 0; 2014 } 2015 else { int dstL = LayoutRegion.distance(subSpace, addingSpace, 2017 dimension, TRAILING, LEADING); 2018 int dstT = LayoutRegion.distance(addingSpace, subSpace, 2019 dimension, TRAILING, LEADING); 2020 if (dstL >= 0 && dstL < distance) 2021 distance = dstL; 2022 if (dstT >= 0 && dstT < distance) 2023 distance = dstT; 2024 2025 if (ortOverlap) { 2026 ortDistance = 0; 2027 inSequence = true; 2028 } 2029 else { dstL = LayoutRegion.distance(subSpace, addingSpace, 2031 dimension^1, TRAILING, LEADING); 2032 dstT = LayoutRegion.distance(addingSpace, subSpace, 2033 dimension^1, TRAILING, LEADING); 2034 if (dstL > 0 && dstL < ortDistance) 2035 ortDistance = dstL; 2036 if (dstT > 0 && dstT < ortDistance) 2037 ortDistance = dstT; 2038 } 2039 } 2040 int point = aEdge < 0 ? CENTER : aEdge; 2041 if (getAddDirection(addingSpace, subSpace, dimension, point) == LEADING) { 2042 if (aEdge != LEADING) 2043 index = i; 2044 break; } 2046 else { parallelWithSequence = false; 2048 if (aEdge == LEADING) 2049 index = i + 1; 2050 } 2051 } 2052 else { parallelWithSequence = true; 2054 } 2055 } 2056 2057 if (inSequence || (dimension == VERTICAL && !parallelWithSequence)) { 2058 if (aSnappedNextTo != null 2060 && (group.isParentOf(aSnappedNextTo) || aSnappedNextTo.getParent() == null)) 2061 { distance = -1; } 2064 IncludeDesc iDesc = addInclusion(group, parallelWithSequence, distance, ortDistance, inclusions); 2065 if (iDesc != null) { 2066 if (index == -1) 2067 index = aEdge == LEADING ? 0 : group.getSubIntervalCount(); 2068 iDesc.index = index; 2069 } 2070 } 2071 } 2072 2073 private static boolean pointInside(LayoutRegion space, int alignment, LayoutInterval group, int dimension) { 2074 LayoutRegion groupSpace = group.getCurrentSpace(); 2075 if (alignment != DEFAULT) { 2076 return LayoutRegion.pointInside(space, alignment, groupSpace, dimension); 2077 } 2078 2079 boolean leadingInside = LayoutRegion.pointInside(space, LEADING, groupSpace, dimension); 2080 boolean trailingInside = LayoutRegion.pointInside(space, TRAILING, groupSpace, dimension); 2081 return (leadingInside && trailingInside) 2082 || (leadingInside && !LayoutInterval.isClosedGroup(group, TRAILING)) 2083 || (trailingInside && !LayoutInterval.isClosedGroup(group, LEADING)); 2084 2097 } 2098 2099 private boolean orthogonalOverlap(LayoutInterval interval) { 2100 boolean ortOverlap; 2101 if (solveOverlap || !LayoutUtils.isOverlapPreventedInOtherDimension(addingInterval, interval, dimension)) { 2102 ortOverlap = LayoutUtils.contentOverlap(addingSpace, interval, dimension^1); 2104 if (ortOverlap 2105 && dragger.isResizing(dimension) && !dragger.isResizing(dimension^1) 2106 && originalPositions1[dimension] != null) 2107 { IncludeDesc original = originalPositions1[dimension]; 2112 LayoutInterval parent = original.parent; 2113 if (parent.isParentOf(interval)) { 2114 if (parent.isParallel() 2115 && (original.neighbor == null 2116 || (original.neighbor != interval && !original.neighbor.isParentOf(interval)))) 2117 ortOverlap = false; 2118 } 2119 else if (parent == interval) { 2120 if (parent.isParallel() && original.neighbor == null) 2121 ortOverlap = false; 2122 } 2123 else if (!interval.isParentOf(parent)) { 2124 parent = LayoutInterval.getCommonParent(parent, interval); 2125 if (parent != null && parent.isParallel()) 2126 ortOverlap = false; 2127 } 2128 } 2129 } 2130 else ortOverlap = false; 2133 return ortOverlap; 2134 } 2135 2136 private IncludeDesc addInclusion(LayoutInterval parent, 2137 boolean subgroup, 2138 int distance, 2139 int ortDistance, 2140 List inclusions) 2141 { 2142 if (!inclusions.isEmpty()) { 2143 int index = inclusions.size() - 1; 2144 IncludeDesc last = (IncludeDesc) inclusions.get(index); 2145 boolean useLast = false; 2146 boolean useNew = false; 2147 2148 boolean ortOverlap1 = last.ortDistance == 0; 2149 boolean ortOverlap2 = ortDistance == 0; 2150 if (ortOverlap1 != ortOverlap2) { 2151 useLast = ortOverlap1; 2152 useNew = ortOverlap2; 2153 } 2154 else if (ortOverlap1) { useLast = useNew = true; 2156 } 2157 else { if (last.ortDistance != ortDistance) { 2159 useLast = last.ortDistance < ortDistance; 2160 useNew = ortDistance < last.ortDistance; 2161 } 2162 else if (last.distance != distance) { 2163 useLast = last.distance < distance; 2164 useNew = distance < last.distance; 2165 } 2166 } 2167 if (!useLast && !useNew) { LayoutInterval parParent = last.parent.isParallel() ? 2169 last.parent : last.parent.getParent(); 2170 useNew = parParent.isParentOf(parent); 2171 useLast = !useNew; 2172 } 2173 2174 if (!useLast) 2175 inclusions.remove(index); 2176 if (!useNew) 2177 return null; 2178 } 2179 2180 IncludeDesc iDesc = new IncludeDesc(); 2181 iDesc.parent = parent; 2182 iDesc.newSubGroup = subgroup; 2183 iDesc.alignment = aEdge; 2184 iDesc.snappedParallel = aSnappedParallel; 2185 if (distance == -1) { 2186 iDesc.snappedNextTo = aSnappedNextTo; 2187 iDesc.fixedPosition = true; 2188 } 2189 iDesc.distance = distance; 2190 iDesc.ortDistance = ortDistance; 2191 inclusions.add(iDesc); 2192 2193 return iDesc; 2194 } 2195 2196 2203 private IncludeDesc addAligningInclusion(List inclusions) { 2204 if (aSnappedParallel == null) 2205 return null; 2206 2207 boolean compatibleFound = false; 2208 for (Iterator it=inclusions.iterator(); it.hasNext(); ) { 2209 IncludeDesc iDesc = (IncludeDesc) it.next(); 2210 if (canAlignWith(aSnappedParallel, iDesc.parent, aEdge)) { 2211 compatibleFound = true; 2212 break; 2213 } 2214 } 2215 if (!compatibleFound) { 2216 IncludeDesc iDesc = new IncludeDesc(); 2217 iDesc.parent = aSnappedParallel.getParent() != null ? 2218 LayoutInterval.getFirstParent(aSnappedParallel, PARALLEL) : 2219 aSnappedParallel; 2220 iDesc.snappedParallel = aSnappedParallel; 2221 iDesc.alignment = aEdge; 2222 inclusions.add(0, iDesc); 2223 return iDesc; 2224 } 2225 else return null; 2226 } 2227 2228 2233 private void mergeParallelInclusions(List inclusions, IncludeDesc original, boolean preserveOriginal) { 2234 IncludeDesc best = null; 2236 boolean bestOriginal = false; 2237 for (Iterator it=inclusions.iterator(); it.hasNext(); ) { 2238 IncludeDesc iDesc = (IncludeDesc) it.next(); 2239 if (original == null || !preserveOriginal || canCombine(iDesc, original)) { 2240 if (best != null) { 2241 boolean originalCompatible = original != null && !preserveOriginal 2242 && iDesc.parent == original.parent; 2243 if (!bestOriginal && originalCompatible) { 2244 best = iDesc; 2245 bestOriginal = true; 2246 } 2247 else if (bestOriginal == originalCompatible) { 2248 LayoutInterval group1 = best.parent.isSequential() ? 2249 best.parent.getParent() : best.parent; 2250 LayoutInterval group2 = iDesc.parent.isSequential() ? 2251 iDesc.parent.getParent() : iDesc.parent; 2252 if (group1.isParentOf(group2)) { 2253 best = iDesc; } 2255 else if (!group2.isParentOf(group1) && iDesc.distance < best.distance) { 2256 best = iDesc; 2257 } 2258 } 2259 } 2260 else { 2261 best = iDesc; 2262 bestOriginal = original != null && !preserveOriginal && iDesc.parent == original.parent; 2263 } 2264 } 2265 } 2266 2267 if (best == null) { assert preserveOriginal; 2269 inclusions.clear(); 2270 inclusions.add(original); 2271 return; 2272 } 2273 2274 LayoutInterval commonGroup = best.parent.isSequential() ? best.parent.getParent() : best.parent; 2275 2276 for (Iterator it=inclusions.iterator(); it.hasNext(); ) { 2278 IncludeDesc iDesc = (IncludeDesc) it.next(); 2279 if (iDesc != best) { 2280 if (!compatibleInclusions(iDesc, best, dimension)) { 2281 it.remove(); 2282 } 2283 else { 2284 LayoutInterval group = iDesc.parent.isSequential() ? 2285 iDesc.parent.getParent() : iDesc.parent; 2286 if (group.isParentOf(commonGroup)) { 2287 LayoutInterval neighbor = iDesc.parent.isSequential() ? 2288 iDesc.parent : iDesc.neighbor; 2289 layoutModel.removeInterval(neighbor); 2290 layoutModel.addInterval(neighbor, commonGroup, -1); 2292 if (group.getSubIntervalCount() == 1) { 2293 LayoutInterval parent = group.getParent(); 2294 LayoutInterval last = layoutModel.removeInterval(group, 0); 2295 operations.addContent(last, parent, layoutModel.removeInterval(group)); 2296 if (commonGroup == last && commonGroup.getParent() == null) commonGroup = parent; 2298 updateReplacedOriginalGroup(commonGroup, null); 2299 } 2300 if (iDesc.parent == group) 2301 iDesc.parent = commonGroup; 2302 } 2303 } 2304 } 2305 } 2306 2307 if (best.parent.isParallel() && best.snappedParallel != null && best.ortDistance != 0 2308 && inclusions.size() > 1) 2309 { inclusions.remove(best); 2311 } 2312 if (inclusions.size() == 1) 2313 return; 2314 2315 LayoutInterval subGroup = null; 2317 LayoutInterval nextTo = null; 2318 List separatedLeading = new LinkedList(); 2319 List separatedTrailing = new LinkedList(); 2320 2321 for (Iterator it=inclusions.iterator(); it.hasNext(); ) { 2322 IncludeDesc iDesc = (IncludeDesc) it.next(); 2323 if (iDesc.parent.isSequential() && iDesc.newSubGroup) { 2324 LayoutInterval parSeq = extractParallelSequence(iDesc.parent, addingSpace, false, iDesc.alignment); 2325 assert parSeq.isParallel(); if (subGroup == null) { 2327 subGroup = parSeq; 2328 } 2329 else { 2330 LayoutInterval sub = layoutModel.removeInterval(parSeq, 0); 2331 layoutModel.addInterval(sub, subGroup, -1); 2332 } 2333 operations.extract(parSeq, DEFAULT, true, separatedLeading, separatedTrailing); 2335 layoutModel.removeInterval(parSeq); 2336 layoutModel.removeInterval(iDesc.parent); 2337 } 2338 } 2339 2340 int extractAlign = DEFAULT; 2341 if (subGroup != null) { 2342 if (separatedLeading.isEmpty()) 2343 extractAlign = TRAILING; 2344 if (separatedTrailing.isEmpty()) 2345 extractAlign = LEADING; 2346 } 2347 2348 LayoutInterval subsubGroup = null; 2353 for (Iterator it=inclusions.iterator(); it.hasNext(); ) { 2354 IncludeDesc iDesc = (IncludeDesc) it.next(); 2355 if (iDesc.parent.isParallel() || !iDesc.newSubGroup) { 2356 addToGroup(iDesc, null, false); 2357 operations.extract(addingInterval, extractAlign, extractAlign == DEFAULT, 2359 separatedLeading, separatedTrailing); 2360 LayoutInterval parent = addingInterval.getParent(); 2361 layoutModel.removeInterval(addingInterval); 2362 layoutModel.removeInterval(parent); 2363 if (extractAlign != DEFAULT && LayoutInterval.getCount(parent, DEFAULT, true) >= 1) { 2364 if (subsubGroup == null) { 2365 subsubGroup = new LayoutInterval(PARALLEL); 2366 subsubGroup.setGroupAlignment(extractAlign); 2367 } 2368 operations.addContent(parent, subsubGroup, -1); 2369 } 2370 } 2371 if (iDesc.snappedNextTo != null) 2372 nextTo = iDesc.snappedNextTo; 2373 if (iDesc != best) 2374 it.remove(); 2375 } 2376 2377 int[] borderPos = commonGroup.getCurrentSpace().positions[dimension]; 2379 int[] neighborPos = (subGroup != null ? subGroup : addingInterval).getCurrentSpace().positions[dimension]; 2380 LayoutInterval commonSeq; 2381 int index; 2382 if (commonGroup.getSubIntervalCount() == 0 && commonGroup.getParent() != null) { 2383 LayoutInterval parent = commonGroup.getParent(); 2385 index = layoutModel.removeInterval(commonGroup); 2386 if (parent.isSequential()) { 2387 commonSeq = parent; 2388 commonGroup = parent.getParent(); 2389 } 2390 else { commonSeq = new LayoutInterval(SEQUENTIAL); 2392 commonSeq.setAlignment(commonGroup.getAlignment()); 2393 layoutModel.addInterval(commonSeq, parent, index); 2394 commonGroup = parent; 2395 index = 0; 2396 } 2397 } 2398 else { 2399 commonSeq = new LayoutInterval(SEQUENTIAL); 2400 layoutModel.addInterval(commonSeq, commonGroup, -1); 2401 index = 0; 2402 } 2403 if (commonSeq.getSubIntervalCount() == 0) { 2404 commonSeq.getCurrentSpace().set(dimension, commonGroup.getCurrentSpace()); 2405 } 2406 updateReplacedOriginalGroup(commonGroup, commonSeq); 2407 2408 LayoutInterval sideGroupLeading = null; 2410 LayoutInterval sideGroupTrailing = null; 2411 if (!separatedLeading.isEmpty()) { 2412 int checkCount = commonSeq.getSubIntervalCount(); sideGroupLeading = operations.addGroupContent( 2414 separatedLeading, commonSeq, index, dimension, LEADING); index += commonSeq.getSubIntervalCount() - checkCount; 2416 } 2417 if (!separatedTrailing.isEmpty()) { 2418 sideGroupTrailing = operations.addGroupContent( 2419 separatedTrailing, commonSeq, index, dimension, TRAILING); } 2421 if (sideGroupLeading != null) { 2422 int checkCount = commonSeq.getSubIntervalCount(); sideGroupLeading.getCurrentSpace().set(dimension, borderPos[LEADING], neighborPos[LEADING]); 2424 operations.optimizeGaps(sideGroupLeading, dimension); 2425 index += commonSeq.getSubIntervalCount() - checkCount; 2426 } 2427 if (sideGroupTrailing != null) { 2428 sideGroupTrailing.getCurrentSpace().set(dimension, neighborPos[TRAILING], borderPos[TRAILING]); 2429 operations.optimizeGaps(sideGroupTrailing, dimension); 2430 } 2431 2432 best.parent = commonSeq; 2434 best.newSubGroup = false; 2435 best.neighbor = null; 2436 2437 LayoutInterval separatingGap; 2438 int gapIdx = index; 2439 if (gapIdx == commonSeq.getSubIntervalCount()) { 2440 gapIdx--; 2441 separatingGap = commonSeq.getSubInterval(gapIdx); 2442 } 2443 else { 2444 separatingGap = commonSeq.getSubInterval(gapIdx); 2445 if (!separatingGap.isEmptySpace()) { 2446 gapIdx--; 2447 if (gapIdx > 0) 2448 separatingGap = commonSeq.getSubInterval(gapIdx); 2449 } 2450 } 2451 if (!separatingGap.isEmptySpace()) 2452 separatingGap = null; 2453 else if (subGroup == null) { 2454 index = gapIdx; 2455 if (index == 0 && !LayoutInterval.isAlignedAtBorder(commonSeq, LEADING)) { 2459 layoutModel.removeInterval(separatingGap); 2460 separatingGap = null; 2461 } 2462 else if (index == commonSeq.getSubIntervalCount()-1 && !LayoutInterval.isAlignedAtBorder(commonSeq, TRAILING)) { 2463 layoutModel.removeInterval(separatingGap); 2464 separatingGap = null; 2465 } 2466 } 2467 2468 best.snappedNextTo = nextTo; 2469 if (nextTo != null) 2470 best.fixedPosition = true; 2471 2472 if (subGroup != null) { 2474 if (separatingGap != null 2475 && (extractAlign == DEFAULT 2476 || (extractAlign == LEADING && index > gapIdx) 2477 || (extractAlign == TRAILING && index <= gapIdx))) 2478 { layoutModel.removeInterval(separatingGap); 2481 if (index >= gapIdx && index > 0) 2482 index--; 2483 } 2484 int subIdx = index; 2485 if (subsubGroup != null && subsubGroup.getSubIntervalCount() > 0) { 2486 LayoutInterval seq = new LayoutInterval(SEQUENTIAL); 2487 seq.setAlignment(best.alignment); 2488 operations.addContent(subsubGroup, seq, 0); 2489 layoutModel.addInterval(seq, subGroup, -1); 2490 best.parent = seq; 2492 index = extractAlign == LEADING ? 0 : seq.getSubIntervalCount(); 2493 } 2494 else { 2495 best.newSubGroup = true; 2496 } 2497 operations.addContent(subGroup, commonSeq, subIdx); 2498 2499 updateMovedOriginalNeighbor(); 2500 } 2501 2502 best.index = index; 2503 } 2504 2505 private static boolean compatibleInclusions(IncludeDesc iDesc1, IncludeDesc iDesc2, int dimension) { 2506 LayoutInterval group1 = iDesc1.parent.isSequential() ? 2507 iDesc1.parent.getParent() : iDesc1.parent; 2508 LayoutInterval group2 = iDesc2.parent.isSequential() ? 2509 iDesc2.parent.getParent() : iDesc2.parent; 2510 if (group1 == group2) 2511 return true; 2512 2513 if (group1.isParentOf(group2)) { 2514 LayoutInterval temp = group1; 2515 group1 = group2; 2516 IncludeDesc itemp = iDesc1; 2518 iDesc2 = itemp; 2520 } 2521 else if (!group2.isParentOf(group1)) { 2522 return false; 2523 } 2524 2525 LayoutInterval neighbor = iDesc2.parent.isSequential() ? iDesc2.parent : iDesc2.neighbor; 2527 if (neighbor == null) 2528 return false; 2529 LayoutRegion spaceToHold = neighbor.getCurrentSpace(); 2530 LayoutRegion spaceAvailable = group1.getCurrentSpace(); 2531 return LayoutRegion.pointInside(spaceToHold, LEADING, spaceAvailable, dimension) 2532 && LayoutRegion.pointInside(spaceToHold, TRAILING, spaceAvailable, dimension); 2533 } 2534 2535 private void updateReplacedOriginalGroup(LayoutInterval newGroup, LayoutInterval newSeq) { 2536 updateReplacedOriginalGroup(originalPositions1[dimension], newGroup, newSeq); 2537 updateReplacedOriginalGroup(originalPositions2[dimension], newGroup, newSeq); 2538 } 2539 2540 private static void updateReplacedOriginalGroup(IncludeDesc iDesc, LayoutInterval newGroup, LayoutInterval newSeq) 2541 { 2542 if (iDesc != null && LayoutInterval.getRoot(newGroup) != LayoutInterval.getRoot(iDesc.parent)) { 2543 if (iDesc.parent.isParallel()) 2544 iDesc.parent = newGroup; 2545 else if (newSeq != null) 2546 iDesc.parent = newSeq; 2547 } 2548 } 2549 2550 private void updateMovedOriginalNeighbor() { 2551 updateMovedOriginalNeighbor(originalPositions1[dimension]); 2552 updateMovedOriginalNeighbor(originalPositions2[dimension]); 2553 } 2554 2555 private static void updateMovedOriginalNeighbor(IncludeDesc iDesc) { 2556 if (iDesc != null && iDesc.neighbor != null) { 2557 iDesc.parent = LayoutInterval.getFirstParent(iDesc.neighbor, PARALLEL); 2558 correctNeighborInSequence(iDesc); 2559 } 2560 } 2561 2562 private boolean mergeSequentialInclusions(IncludeDesc iDesc1, IncludeDesc iDesc2) { 2563 if (iDesc2 == null || !canCombine(iDesc1, iDesc2)) 2564 return false; 2565 2566 assert (iDesc1.alignment == LEADING || iDesc1.alignment == TRAILING) 2567 && (iDesc2.alignment == LEADING || iDesc2.alignment == TRAILING) 2568 && iDesc1.alignment == (iDesc2.alignment^1); 2569 2570 if (iDesc1.parent == iDesc2.parent) 2571 return true; 2572 2573 LayoutInterval commonGroup; 2574 boolean nextTo; 2575 if (iDesc1.parent.isParentOf(iDesc2.parent)) { 2576 commonGroup = iDesc1.parent; 2577 nextTo = iDesc1.neighbor != null || iDesc2.snappedNextTo != null; 2578 } 2579 else if (iDesc2.parent.isParentOf(iDesc1.parent)) { 2580 commonGroup = iDesc2.parent; 2581 nextTo = iDesc2.neighbor != null || iDesc1.snappedNextTo != null; 2582 } 2583 else { 2584 commonGroup = LayoutInterval.getFirstParent(iDesc1.parent, SEQUENTIAL); 2585 nextTo = false; 2586 } 2587 2588 if (commonGroup.isSequential() || nextTo) { 2589 if (iDesc1.alignment == TRAILING) { 2591 IncludeDesc temp = iDesc1; 2592 iDesc1 = iDesc2; 2593 iDesc2 = temp; 2594 } 2596 int startIndex = 0; 2597 LayoutInterval ext1 = null; 2598 boolean startGap = false; 2599 int endIndex = 0; 2600 LayoutInterval ext2 = null; 2601 boolean endGap = false; 2602 2603 if (commonGroup.isSequential()) { 2604 if (commonGroup.isParentOf(iDesc1.parent)) { 2605 ext1 = iDesc1.parent.isSequential() ? iDesc1.parent : iDesc1.neighbor; 2606 if (ext1 != null) { 2607 while (ext1.getParent().getParent() != commonGroup) { 2608 ext1 = ext1.getParent(); 2609 } 2610 startIndex = commonGroup.indexOf(ext1.getParent()); 2611 } 2612 else { LayoutInterval inCommon = iDesc1.parent; 2614 while (inCommon.getParent() != commonGroup) { 2615 inCommon = inCommon.getParent(); 2616 } 2617 startIndex = commonGroup.indexOf(inCommon); 2618 } 2619 } 2620 else { 2621 startIndex = iDesc1.index; 2622 if (startIndex == commonGroup.getSubIntervalCount()) 2623 startIndex--; 2624 startGap = commonGroup.getSubInterval(startIndex).isEmptySpace(); 2625 } 2626 2627 if (commonGroup.isParentOf(iDesc2.parent)) { 2628 ext2 = iDesc2.parent.isSequential() ? iDesc2.parent : iDesc2.neighbor; 2629 if (ext2 != null) { 2630 while (ext2.getParent().getParent() != commonGroup) { 2631 ext2 = ext2.getParent(); 2632 } 2633 endIndex = commonGroup.indexOf(ext2.getParent()); 2634 } 2635 else { 2636 LayoutInterval inCommon = iDesc2.parent; 2637 while (inCommon.getParent() != commonGroup) { 2638 inCommon = inCommon.getParent(); 2639 } 2640 endIndex = commonGroup.indexOf(inCommon); 2641 } 2642 } 2643 else { 2644 endIndex = iDesc2.index; 2645 if (iDesc2.snappedParallel == null || !commonGroup.isParentOf(iDesc2.snappedParallel)) 2646 endGap = commonGroup.getSubInterval(--endIndex).isEmptySpace(); 2647 } 2648 } 2649 2650 if ((endIndex > startIndex + 1 || (endIndex == startIndex+1 && !startGap && !endGap)) 2651 && ((ext1 != null && !iDesc1.newSubGroup) || (ext2 != null && !iDesc2.newSubGroup))) 2652 { LayoutInterval parGroup; 2654 if (startIndex == 0 && endIndex == commonGroup.getSubIntervalCount()-1) { 2655 parGroup = commonGroup.getParent(); 2657 } 2658 else { parGroup = new LayoutInterval(PARALLEL); 2660 LayoutInterval parSeq = new LayoutInterval(SEQUENTIAL); 2661 layoutModel.addInterval(parSeq, parGroup, 0); 2662 parGroup.getCurrentSpace().set(dimension, 2663 LayoutUtils.getVisualPosition(commonGroup.getSubInterval(startIndex), dimension, LEADING), 2664 LayoutUtils.getVisualPosition(commonGroup.getSubInterval(endIndex), dimension, TRAILING)); 2665 int i = startIndex; 2666 while (i <= endIndex) { 2667 LayoutInterval li = layoutModel.removeInterval(commonGroup, i); 2668 endIndex--; 2669 layoutModel.addInterval(li, parSeq, -1); 2670 } 2671 layoutModel.addInterval(parGroup, commonGroup, startIndex); 2672 } 2673 LayoutInterval extSeq = new LayoutInterval(SEQUENTIAL); layoutModel.addInterval(extSeq, parGroup, -1); 2675 if (ext1 != null) { 2676 LayoutInterval parent = ext1.getParent(); 2677 layoutModel.removeInterval(ext1); 2678 if (parent.getSubIntervalCount() == 1) { 2679 LayoutInterval last = layoutModel.removeInterval(parent, 0); 2680 operations.addContent(last, parent.getParent(), layoutModel.removeInterval(parent)); 2681 } 2682 operations.addContent(ext1, extSeq, 0); 2683 if (ext2 != null) { 2684 LayoutInterval gap = new LayoutInterval(SINGLE); 2685 int size = LayoutRegion.distance(ext1.getCurrentSpace(), ext2.getCurrentSpace(), dimension, LEADING, TRAILING); 2686 gap.setSize(size); 2687 layoutModel.addInterval(gap, extSeq, -1); 2688 2689 iDesc1.index = iDesc2.index = extSeq.indexOf(gap); 2690 } 2691 else iDesc2.index = iDesc1.index; 2692 } 2693 else iDesc1.index = iDesc2.index; 2694 if (ext2 != null) { 2695 LayoutInterval parent = ext2.getParent(); 2696 layoutModel.removeInterval(ext2); 2697 if (parent.getSubIntervalCount() == 1) { 2698 LayoutInterval last = layoutModel.removeInterval(parent, 0); 2699 operations.addContent(last, parent.getParent(), layoutModel.removeInterval(parent)); 2700 } 2701 operations.addContent(ext2, extSeq, -1); 2702 } 2703 2704 iDesc1.parent = iDesc2.parent = extSeq; 2705 iDesc1.newSubGroup = iDesc2.newSubGroup = false; 2706 iDesc1.neighbor = iDesc2.neighbor = null; 2707 } 2708 else { if (iDesc2.parent.isParentOf(iDesc1.parent)) { 2710 iDesc2.parent = iDesc1.parent; 2711 iDesc2.index = iDesc1.index; 2712 iDesc2.newSubGroup = iDesc1.newSubGroup; 2713 iDesc2.neighbor = iDesc1.neighbor; 2714 if (endGap) iDesc2.fixedPosition = false; 2716 } 2717 else if (iDesc1.parent.isParentOf(iDesc2.parent)) { 2718 iDesc1.parent = iDesc2.parent; 2719 iDesc1.index = iDesc2.index; 2720 iDesc1.newSubGroup = iDesc2.newSubGroup; 2721 iDesc1.neighbor = iDesc2.neighbor; 2722 if (startGap) iDesc1.fixedPosition = false; 2724 } 2725 } 2726 } 2727 else { assert iDesc1.parent.isParallel() && iDesc2.parent.isParallel() 2729 && (commonGroup == iDesc1.parent || commonGroup == iDesc2.parent) 2730 && iDesc1.neighbor == null && iDesc2.neighbor == null; 2731 2732 if ((iDesc2.snappedNextTo == null && iDesc2.snappedParallel == null) 2733 || (iDesc2.snappedParallel != null && canAlignWith(iDesc2.snappedParallel, iDesc1.parent, iDesc2.alignment))) 2734 { iDesc2.parent = iDesc1.parent; 2736 return true; 2737 } 2738 2739 if (iDesc2.parent == commonGroup) { 2740 IncludeDesc temp = iDesc1; 2741 iDesc1 = iDesc2; 2742 iDesc2 = temp; 2743 } assert iDesc2.snappedNextTo == null; 2745 2746 if (iDesc2.snappedParallel == iDesc2.parent) { 2747 iDesc2.parent = LayoutInterval.getFirstParent(iDesc2.parent, PARALLEL); 2748 if (iDesc2.parent == iDesc1.parent) 2749 return true; 2750 } 2751 2752 if (iDesc2.snappedParallel == null || canAlignWith(iDesc2.snappedParallel, iDesc1.parent, iDesc2.alignment)) { 2753 iDesc2.parent = iDesc1.parent; 2755 return true; 2756 } 2757 2758 if (LayoutInterval.isAlignedAtBorder(iDesc2.parent, iDesc1.parent, iDesc1.alignment)) { 2759 iDesc1.parent = iDesc2.parent; 2760 return true; } 2762 2763 LayoutInterval seq = iDesc2.parent.getParent(); 2764 if (seq.isSequential() && seq.getParent() == iDesc1.parent) { 2765 int index = seq.indexOf(iDesc2.parent) + (iDesc1.alignment == LEADING ? -1 : 1); 2766 LayoutInterval gap = (index == 0 || index == seq.getSubIntervalCount()-1) ? 2767 seq.getSubInterval(index) : null; 2768 if (gap != null 2769 && LayoutInterval.isFixedDefaultPadding(gap) 2770 && iDesc1.snappedNextTo == iDesc1.parent 2771 && LayoutInterval.wantResize(seq)) 2772 { iDesc1.parent = iDesc2.parent; 2774 iDesc1.snappedNextTo = null; 2775 iDesc1.snappedParallel = iDesc2.parent; 2776 return true; 2777 } 2778 2779 if (gap != null && gap.isEmptySpace() && iDesc1.snappedParallel == iDesc1.parent) { 2780 int gapSize = LayoutInterval.getIntervalCurrentSize(gap, dimension); 2782 copyGapInsideGroup(gap, gapSize, iDesc2.parent, iDesc1.alignment); 2783 layoutModel.removeInterval(gap); 2784 iDesc1.parent = iDesc2.parent; 2785 return true; 2786 } 2787 } 2788 2789 iDesc2.parent = iDesc1.parent; } 2791 2792 return true; 2793 } 2794 2795 2801 private void copyGapInsideGroup(LayoutInterval gap, int gapSize, LayoutInterval group, int alignment) { 2802 assert gap.isEmptySpace() && (alignment == LEADING || alignment == TRAILING); 2803 2804 if (alignment == LEADING) 2805 gapSize = -gapSize; 2806 2807 group.getCurrentSpace().positions[dimension][alignment] += gapSize; 2808 2809 for (Iterator it=group.getSubIntervals(); it.hasNext(); ) { 2810 LayoutInterval sub = (LayoutInterval) it.next(); 2811 LayoutInterval gapClone = LayoutInterval.cloneInterval(gap, null); 2812 if (sub.isSequential()) { 2813 sub.getCurrentSpace().positions[dimension][alignment] += gapSize; 2814 int index = alignment == LEADING ? 0 : sub.getSubIntervalCount(); 2815 operations.insertGapIntoSequence(gapClone, sub, index, dimension); 2816 } 2817 else { 2818 LayoutInterval seq = new LayoutInterval(SEQUENTIAL); 2819 seq.getCurrentSpace().set(dimension, sub.getCurrentSpace()); 2820 seq.getCurrentSpace().positions[dimension][alignment] += gapSize; 2821 seq.setAlignment(sub.getRawAlignment()); 2822 layoutModel.addInterval(seq, group, layoutModel.removeInterval(sub)); 2823 layoutModel.setIntervalAlignment(sub, DEFAULT); 2824 layoutModel.addInterval(sub, seq, 0); 2825 layoutModel.addInterval(gapClone, seq, alignment == LEADING ? 0 : 1); 2826 } 2827 } 2828 } 2829 2830 private boolean shouldEnterGroup(LayoutInterval group) { 2831 assert group.isParallel(); 2832 2833 int alignment = aEdge; 2834 int groupAlign = group.getGroupAlignment(); 2835 if (groupAlign != alignment 2836 && ((groupAlign != LEADING && groupAlign != TRAILING) 2837 || (alignment != LEADING && alignment != TRAILING && alignment != DEFAULT))) 2838 return false; 2840 if (aSnappedParallel != null && !allowsSubAlignWith(aSnappedParallel, group, alignment)) 2841 return false; 2843 return true; 2844 } 2845 2846 2851 private boolean allowsSubAlignWith(LayoutInterval interval, LayoutInterval group, int alignment) { 2852 if (group == interval || group.isParentOf(interval)) 2853 return true; 2854 2855 LayoutInterval parent = LayoutInterval.getFirstParent(interval, PARALLEL); 2856 while (parent != null) { 2857 if (LayoutInterval.isAlignedAtBorder(interval, parent, alignment)) { 2858 if (parent.isParentOf(group) && LayoutInterval.isAlignedAtBorder(group, parent, alignment)) { 2859 return true; 2860 } 2861 interval = parent; 2862 parent = LayoutInterval.getFirstParent(interval, PARALLEL); 2863 } 2864 else parent = null; 2865 } 2866 2867 return false; 2868 } 2869 2870 2874 private boolean canAlignWith(LayoutInterval interval, LayoutInterval group, int alignment) { 2875 if (group.isSequential()) 2876 group = group.getParent(); 2877 2878 if (interval == group) 2879 return true; 2881 LayoutInterval parent = interval.getParent(); 2882 if (parent == null) 2883 parent = interval; 2884 else if (parent.isSequential()) 2885 parent = parent.getParent(); 2886 2887 while (parent != null && parent != group && !parent.isParentOf(group)) { 2888 if (canSubstAlignWithParent(interval, dimension, alignment, dragger.isResizing(dimension))) { 2889 interval = parent; 2890 parent = LayoutInterval.getFirstParent(interval, PARALLEL); 2891 } 2892 else parent = null; 2893 } 2894 if (parent == null) 2895 return false; 2896 if (parent == group) 2897 return true; 2898 return LayoutInterval.isAlignedAtBorder(group, parent, alignment); 2900 } 2902 2903 private static boolean canSubstAlignWithParent(LayoutInterval interval, int dimension, int alignment, boolean placedAtBorderEnough) { 2904 LayoutInterval parent = LayoutInterval.getFirstParent(interval, PARALLEL); 2905 boolean aligned = LayoutInterval.isAlignedAtBorder(interval, parent, alignment); 2906 if (!aligned 2907 && LayoutInterval.getDirectNeighbor(interval, alignment, false) == null 2908 && LayoutInterval.isPlacedAtBorder(interval, parent, dimension, alignment)) 2909 { aligned = placedAtBorderEnough 2911 || LayoutInterval.getDirectNeighbor(parent, alignment, true) != null 2912 || LayoutInterval.isClosedGroup(parent, alignment); 2913 if (!aligned) { boolean allTouching = true; 2915 for (Iterator it=parent.getSubIntervals(); it.hasNext(); ) { 2916 LayoutInterval li = (LayoutInterval) it.next(); 2917 if (li.getAlignment() == alignment || LayoutInterval.wantResize(li)) { 2918 aligned = true; 2919 break; 2920 } 2921 else if (allTouching && !LayoutInterval.isPlacedAtBorder(li, dimension, alignment)) { 2922 allTouching = false; 2923 } 2924 } 2925 if (allTouching) 2926 aligned = true; 2927 } 2928 } 2929 return aligned; 2930 } 2931 2932 private boolean canCombine(IncludeDesc iDesc1, IncludeDesc iDesc2) { 2933 if (iDesc1.parent == iDesc2.parent) 2934 return true; 2935 2936 LayoutInterval commonGroup; 2937 if (iDesc1.parent.isParentOf(iDesc2.parent)) 2938 return isBorderInclusion(iDesc2); 2939 else if (iDesc2.parent.isParentOf(iDesc1.parent)) 2940 return isBorderInclusion(iDesc1); 2941 else { 2942 LayoutInterval parParent1 = iDesc1.parent.isParallel() ? iDesc1.parent : iDesc1.parent.getParent(); 2943 LayoutInterval parParent2 = iDesc2.parent.isParallel() ? iDesc2.parent : iDesc2.parent.getParent(); 2944 return parParent1.getParent() == parParent2.getParent() 2945 && isBorderInclusion(iDesc1) 2946 && isBorderInclusion(iDesc2) 2947 && LayoutInterval.getDirectNeighbor(parParent1, iDesc1.alignment^1, true) == parParent2; 2948 } 2949 } 2950 2951 private boolean isBorderInclusion(IncludeDesc iDesc) { 2952 if (iDesc.alignment != LEADING && iDesc.alignment != TRAILING) 2953 return false; 2954 2955 if (iDesc.parent.isSequential()) { 2956 int startIndex = iDesc.alignment == LEADING ? iDesc.index : 0; 2957 int endIndex = iDesc.alignment == LEADING ? iDesc.parent.getSubIntervalCount() - 1 : iDesc.index - 1; 2958 return startIndex > endIndex 2959 || !LayoutUtils.contentOverlap(addingSpace, iDesc.parent, startIndex, endIndex, dimension^1); 2960 } 2961 2962 return iDesc.neighbor == null 2963 || (iDesc.alignment == LEADING && iDesc.index >= 1) 2964 || (iDesc.alignment == TRAILING && iDesc.index == 0); 2965 } 2966 2967 private static int getAddDirection(LayoutRegion adding, 2968 LayoutRegion existing, 2969 int dimension, 2970 int alignment) 2971 { 2972 return LayoutRegion.distance(adding, existing, dimension, alignment, CENTER) > 0 ? 2973 LEADING : TRAILING; 2974 } 2975} 2976 | Popular Tags |