1 19 20 package org.netbeans.modules.form.layoutdesign; 21 22 23 import java.awt.BasicStroke ; 24 import java.awt.Dimension ; 25 import java.awt.Graphics2D ; 26 import java.awt.Image ; 27 import java.awt.Point ; 28 import java.awt.Rectangle ; 29 import java.awt.RenderingHints ; 30 import java.awt.Stroke ; 31 import java.util.*; 32 import org.openide.loaders.DataObject; 33 import org.openide.util.Utilities; 34 35 public class LayoutDesigner implements LayoutConstants { 36 37 private LayoutModel layoutModel; 38 39 private VisualMapper visualMapper; 40 41 private LayoutDragger dragger; 42 43 private LayoutOperations operations; 44 45 private Listener modelListener; 46 47 private boolean imposeSize = true; 48 private boolean optimizeStructure = true; 49 private boolean visualStateUpToDate; 50 51 53 public LayoutDesigner(LayoutModel model, VisualMapper mapper) { 54 layoutModel = model; 55 visualMapper = mapper; 56 operations = new LayoutOperations(model, mapper); 57 modelListener = new Listener(); 58 modelListener.activate(); 59 } 60 61 64 public boolean updateCurrentState() { 65 if (logTestCode()) { 66 testCode.add("// > UPDATE CURRENT STATE"); } 68 Object changeMark = layoutModel.getChangeMark(); 69 boolean changeRequired = imposeSize || optimizeStructure; 70 Set updatedContainers = changeRequired ? new HashSet() : null; 71 try { 72 if (changeRequired) { 73 modelListener.deactivate(); destroyRedundantGroups(updatedContainers); 75 operations.mergeAdjacentGaps(updatedContainers); 76 } 77 78 updatePositions(updatedContainers); 79 } finally { 80 if (changeRequired) { 81 modelListener.activate(); 82 } 83 } 84 85 if (changeRequired) { 86 imposeSize = optimizeStructure = false; 87 88 if (updatedContainers != null) { 89 Iterator it = updatedContainers.iterator(); 90 while (it.hasNext()) { 91 LayoutComponent cont = (LayoutComponent) it.next(); 92 visualMapper.rebuildLayout(cont.getId()); 93 } 94 updatePositions(null); 95 } 96 } 97 98 if (logTestCode()) { 99 testCode.add("ld.updateCurrentState();"); testCode.add("// < UPDATE CURRENT STATE"); } 102 103 visualStateUpToDate = true; 104 return !changeMark.equals(layoutModel.getChangeMark()); 105 } 106 107 public void externalSizeChangeHappened() { 108 imposeSize = true; 109 visualStateUpToDate = false; 110 if (logTestCode()) { 111 testCode.add("ld.externalSizeChangeHappened();"); } 113 } 114 115 void requireStructureOptimization() { 116 optimizeStructure = true; 117 visualStateUpToDate = false; 118 119 Iterator it = layoutModel.getAllComponents(); 120 while (it.hasNext()) { 121 LayoutComponent comp = (LayoutComponent) it.next(); 122 if (comp.isLayoutContainer()) { 123 for (int i=0; i < DIM_COUNT; i++) { 124 cleanDesignAttrs(comp.getLayoutRoot(i)); 125 } 126 } 127 } 128 } 129 130 private void updatePositions(Set updatedContainers) { 131 Iterator it = layoutModel.getAllComponents(); 132 while (it.hasNext()) { 133 LayoutComponent comp = (LayoutComponent) it.next(); 134 if (!comp.isLayoutContainer()) 135 continue; 136 137 if (optimizeStructure || imposeSize) { 138 if (imposeCurrentContainerSize(comp, null, false)) { 140 updatedContainers.add(comp); 141 for (int i=0; i < DIM_COUNT; i++) { 142 LayoutInterval root = comp.getLayoutRoot(i); 143 if (optimizeStructure) { 144 optimizeGaps(root, i, true); 145 destroyRedundantGroups(root); 146 } 147 updateDesignModifications(root, i); 148 } 149 } 150 } 151 else { Rectangle bounds = visualMapper.getContainerInterior(comp.getId()); 153 if (bounds != null) { 154 comp.setCurrentInterior(bounds); 155 Iterator it2 = comp.getSubcomponents(); 156 while (it2.hasNext()) { 157 LayoutComponent subComp = (LayoutComponent) it2.next(); 158 bounds = visualMapper.getComponentBounds(subComp.getId()); 159 int baseline = visualMapper.getBaselinePosition(subComp.getId(), bounds.width, bounds.height); 160 subComp.setCurrentBounds(bounds, baseline); 161 } 162 for (int i=0; i < DIM_COUNT; i++) { 163 updateLayoutStructure(comp.getLayoutRoot(i), i, false); 164 } 165 } 166 } 167 } 168 } 169 170 177 void updateLayoutStructure(LayoutInterval interval, int dimension, boolean imposeGaps) { 178 LayoutRegion space = interval.getCurrentSpace(); 179 boolean baseline = interval.getGroupAlignment() == BASELINE; 180 181 boolean first = true; 182 boolean firstResizingSpace = false; 183 int leadingSpace = 0; 184 boolean skipNext = false; 185 186 Iterator it = interval.getSubIntervals(); 187 while (it.hasNext()) { 188 LayoutInterval sub = (LayoutInterval) it.next(); 189 if (sub.isEmptySpace()) { 190 if (!interval.isSequential()) { 191 assert interval.getParent() == null; 193 if (imposeGaps && space.isSet(dimension)) { 194 imposeCurrentGapSize(sub, space.size(dimension), dimension); 195 } 196 } 197 else if (first || !it.hasNext()) { 198 int min = sub.getMinimumSize(true); 200 int pref = sub.getPreferredSize(true); 201 int max = sub.getMaximumSize(true); 202 if ((min == pref || min == USE_PREFERRED_SIZE) 203 && (pref == max || max == USE_PREFERRED_SIZE) 204 && pref != NOT_EXPLICITLY_DEFINED) { 205 if (first) { 207 leadingSpace = pref; 208 } else { 209 space.reshape(dimension, TRAILING, pref); 210 } 211 } else { 212 int currentPref = LayoutRegion.UNKNOWN; 213 LayoutInterval sibling = LayoutInterval.getNeighbor(sub, SEQUENTIAL, first ? LEADING : TRAILING); 214 if (sibling == null) { 215 LayoutRegion rootSpace = LayoutInterval.getRoot(interval).getCurrentSpace(); 216 if (first) { 217 firstResizingSpace = true; 218 leadingSpace = - rootSpace.positions[dimension][LEADING]; 219 } else { currentPref = rootSpace.positions[dimension][TRAILING] - space.positions[dimension][TRAILING]; 221 space.reshape(dimension, TRAILING, currentPref); 222 } 223 } else if (sibling.isEmptySpace()) { 224 LayoutInterval parent = interval.getParent(); 225 LayoutInterval alignedParent = parent; 226 int align = first ? LEADING : TRAILING; 227 while (parent != null && LayoutInterval.isAlignedAtBorder(interval, parent, align)) { 228 alignedParent = parent; 229 parent = parent.getParent(); 230 } 231 LayoutInterval parComp = LayoutUtils.getOutermostComponent(alignedParent, dimension, align); 232 LayoutRegion parSpace = parComp.getCurrentSpace(); 235 if (first) { 236 firstResizingSpace = true; 237 leadingSpace = - parSpace.positions[dimension][LEADING]; 238 } 239 else { 240 currentPref = parSpace.positions[dimension][TRAILING] - space.positions[dimension][TRAILING]; 241 space.reshape(dimension, TRAILING, currentPref); 242 } 243 } else { 244 if (first) { 245 firstResizingSpace = true; 246 LayoutRegion sibSpace = sibling.getCurrentSpace(); 247 leadingSpace = - sibSpace.positions[dimension][TRAILING]; 248 } else { 249 LayoutRegion sibSpace = sibling.getCurrentSpace(); 250 if (!sibling.isComponent()) { 251 sibSpace.reset(); 252 updateLayoutStructure(sibling, dimension, imposeGaps); 253 skipNext = true; 254 } 255 int sibPos = sibSpace.positions[dimension][LEADING]; 256 if (sibPos != LayoutRegion.UNKNOWN) { currentPref = sibPos - space.positions[dimension][TRAILING]; 258 space.reshape(dimension, TRAILING, currentPref); 259 } 260 } 261 } 262 263 if (imposeGaps && (currentPref != LayoutRegion.UNKNOWN)) { imposeCurrentGapSize(sub, currentPref, dimension); 265 } 266 } 267 } 268 else if (imposeGaps) { 269 LayoutInterval sibling = LayoutInterval.getDirectNeighbor(sub, TRAILING, false); 270 assert !sibling.isEmptySpace(); 271 LayoutRegion sibSpace = sibling.getCurrentSpace(); 272 if (!sibling.isComponent()) { 273 sibSpace.reset(); 274 updateLayoutStructure(sibling, dimension, imposeGaps); 275 skipNext = true; 276 } 277 int currentSize = LayoutRegion.distance(space, sibSpace, dimension, TRAILING, LEADING); 278 imposeCurrentGapSize(sub, currentSize, dimension); 279 } 280 first = false; 281 continue; 282 } 283 284 LayoutRegion subSpace = sub.getCurrentSpace(); 285 if (skipNext) { skipNext = false; 287 } 288 else if (sub.isGroup()) { 289 assert sub.getSubIntervalCount() > 0; subSpace.reset(); 291 294 updateLayoutStructure(sub, dimension, imposeGaps); 295 } 296 space.expand(subSpace); 297 298 if (baseline && sub.isComponent()) { 299 int baselinePos = subSpace.positions[dimension][BASELINE]; 300 if (baselinePos != LayoutRegion.UNKNOWN) { 301 space.positions[dimension][BASELINE] = baselinePos; 302 baseline = false; 303 } 304 } 305 if (firstResizingSpace) { 306 leadingSpace += space.positions[dimension][LEADING]; 307 firstResizingSpace = false; 308 if (imposeGaps) { 309 imposeCurrentGapSize(interval.getSubInterval(0), leadingSpace, dimension); 310 } 311 } 312 first = false; 313 } 314 if (leadingSpace != 0) { 315 space.reshape(dimension, LEADING, -leadingSpace); 316 } 317 } 318 319 public void dumpTestcode(DataObject form) { 320 LayoutTestUtils.dumpTestcode(testCode, form, getModelCounter()); 321 testCode = new ArrayList(); 322 testCode0 = new ArrayList(); 323 beforeMove = new ArrayList(); 324 move1 = new ArrayList(); 325 move2 = new ArrayList(); 326 isMoving = false; 327 } 328 329 332 public void startAdding(LayoutComponent[] comps, 333 Rectangle [] bounds, 334 Point hotspot, 335 String defaultContId) 336 { 337 if (logTestCode()) { 338 testCode.add("// > START ADDING"); } 340 prepareDragger(comps, bounds, hotspot, LayoutDragger.ALL_EDGES); 341 if (logTestCode()) { 342 testCode.add("{"); LayoutTestUtils.writeLayoutComponentArray(testCode, "comps", "lc"); LayoutTestUtils.writeRectangleArray(testCode, "bounds", bounds); LayoutTestUtils.writeString(testCode, "defaultContId", defaultContId); testCode.add("Point hotspot = new Point(" + new Double (hotspot.getX()).intValue() + "," + new Double (hotspot.getY()).intValue() + ");"); testCode.add("ld.startAdding(comps, bounds, hotspot, defaultContId);"); testCode.add("}"); } 352 if (defaultContId != null) 353 dragger.setTargetContainer(layoutModel.getLayoutComponent(defaultContId)); 354 if (logTestCode()) { 355 testCode.add("// < START ADDING"); } 357 } 358 359 public void startMoving(String [] compIds, Rectangle [] bounds, Point hotspot) { 360 if (logTestCode()) { 361 testCode.add("// > START MOVING"); } 363 364 LayoutComponent[] comps = new LayoutComponent[compIds.length]; 365 for (int i=0; i < compIds.length; i++) { 366 comps[i] = layoutModel.getLayoutComponent(compIds[i]); 367 } 368 prepareDragger(comps, bounds, hotspot, LayoutDragger.ALL_EDGES); 369 370 if (logTestCode()) { 371 testCode.add("{"); LayoutTestUtils.writeStringArray(testCode, "compIds", compIds); LayoutTestUtils.writeRectangleArray(testCode, "bounds", bounds); testCode.add("Point hotspot = new Point(" + new Double (hotspot.getX()).intValue() + "," + new Double (hotspot.getY()).intValue() + ");"); testCode.add("ld.startMoving(compIds, bounds, hotspot);"); testCode.add("}"); } 379 380 dragger.setTargetContainer(comps[0].getParent()); 381 382 if (logTestCode()) { 383 testCode.add("// < START MOVING"); } 385 } 386 387 public void startResizing(String [] compIds, 389 Rectangle [] bounds, 390 Point hotspot, 391 int[] resizeEdges, 392 boolean inLayout) 393 { 394 if (logTestCode()) { 395 testCode.add("// > START RESIZING"); } 397 398 LayoutComponent[] comps = new LayoutComponent[compIds.length]; 399 for (int i=0; i < compIds.length; i++) { 400 comps[i] = layoutModel.getLayoutComponent(compIds[i]); 401 } 402 403 int[] edges = new int[DIM_COUNT]; 404 for (int i=0; i < DIM_COUNT; i++) { 405 edges[i] = resizeEdges[i] == LEADING || resizeEdges[i] == TRAILING ? 406 resizeEdges[i] : LayoutRegion.NO_POINT; 407 } 408 409 prepareDragger(comps, bounds, hotspot, edges); 410 411 if (logTestCode()) { 412 testCode.add("{"); LayoutTestUtils.writeStringArray(testCode, "compIds", compIds); LayoutTestUtils.writeRectangleArray(testCode, "bounds", bounds); testCode.add("Point hotspot = new Point(" + new Double (hotspot.getX()).intValue() + "," + new Double (hotspot.getY()).intValue() + ");"); LayoutTestUtils.writeIntArray(testCode, "resizeEdges", resizeEdges); testCode.add("boolean inLayout = " + inLayout + ";"); testCode.add("ld.startResizing(compIds, bounds, hotspot, resizeEdges, inLayout);"); testCode.add("}"); } 422 423 dragger.setTargetContainer(inLayout ? comps[0].getParent() : null); 424 425 if (logTestCode()) { 426 testCode.add("// < START RESIZING"); } 428 } 429 430 private void prepareDragger(LayoutComponent[] comps, 431 Rectangle [] bounds, 432 Point hotspot, 433 int[] edges) 434 { 435 if (comps.length != bounds.length) 436 throw new IllegalArgumentException (); 437 438 LayoutRegion[] movingFormation = new LayoutRegion[bounds.length]; 439 for (int i=0; i < bounds.length; i++) { 440 int baseline = visualMapper.getBaselinePosition(comps[i].getId(), bounds[i].width, bounds[i].height); 441 int baselinePos = baseline > 0 ? bounds[i].y + baseline : LayoutRegion.UNKNOWN; 442 movingFormation[i] = new LayoutRegion(); 443 movingFormation[i].set(bounds[i], baselinePos); 444 } 445 446 dragger = new LayoutDragger(comps, 447 movingFormation, 448 new int[] { hotspot.x, hotspot.y }, 449 edges, 450 visualMapper); 451 } 452 453 467 public void move(Point p, 468 String containerId, 469 boolean autoPositioning, 470 boolean lockDimension, 471 Rectangle [] bounds) 472 { 473 474 int x = (p != null) ? p.x : 0; 475 int y = (p != null) ? p.y : 0; 476 477 if (logTestCode()) { 478 if (!isMoving) { 480 isMoving = true; 481 beforeMove = new ArrayList(); 483 beforeMove.addAll(testCode); 484 testCode = new ArrayList(); 485 lastMovePoint = new Point (0,0); 486 } 487 488 if (!((x == lastMovePoint.x) && (y == lastMovePoint.y))) { 489 lastMovePoint = new Point (x, y); 490 move1 = move2; 491 testCode0 = testCode; 492 } 493 494 move2 = new ArrayList(); 495 move2.add("// > MOVE"); 496 testCode = new ArrayList(); 497 } 498 if ((!visualStateUpToDate) || (dragger == null)) { 499 return; } 501 502 if (!dragger.isResizing() && (!lockDimension || dragger.getTargetContainer() == null)) { 503 dragger.setTargetContainer(layoutModel.getLayoutComponent(containerId)); 504 } 505 506 cursorPos[HORIZONTAL] = p.x; 507 cursorPos[VERTICAL] = p.y; 508 509 dragger.move(cursorPos, autoPositioning, lockDimension); 510 511 p.x = cursorPos[HORIZONTAL]; 512 p.y = cursorPos[VERTICAL]; 513 514 if (bounds != null) { 515 LayoutRegion[] current = dragger.getMovingBounds(); 516 for (int i=0; i < current.length; i++) { 517 current[i].toRectangle(bounds[i]); 518 } 519 } 520 521 if (logTestCode()) { 522 move2.add("{"); move2.add("Point p = new Point(" + x + "," + y + ");"); LayoutTestUtils.writeString(move2, "containerId", containerId); move2.add("boolean autoPositioning = " + autoPositioning + ";"); move2.add("boolean lockDimension = " + lockDimension + ";"); LayoutTestUtils.writeRectangleArray(move2, "bounds", bounds); move2.add("ld.move(p, containerId, autoPositioning, lockDimension, bounds);"); move2.add("}"); move2.add("// < MOVE"); } 532 } 533 534 public void endMoving(boolean committed) { 535 if (!committed && dragger == null) 536 return; 538 if (logTestCode()) { 539 if (committed) { 540 beforeMove.addAll(testCode0); 541 beforeMove.addAll(move1); 542 beforeMove.addAll(testCode); 543 beforeMove.addAll(move2); 544 testCode = beforeMove; 545 } 546 testCode.add("// > END MOVING"); isMoving = false; 548 } 549 try { 550 if (committed) { 551 LayoutComponent[] components = dragger.getMovingComponents(); 552 LayoutComponent targetContainer = dragger.getTargetContainer(); 553 554 if (targetContainer != null) { 555 boolean newComponent = components[0].getParent() == null; 556 LayoutInterval[] addingInts = new LayoutInterval[DIM_COUNT]; 558 LayoutRegion origSpace = null; 559 for (int dim=0; dim < DIM_COUNT; dim++) { 560 if (components.length > 1) { 561 if (newComponent) { 562 for (int i=0; i<components.length; i++) { 564 layoutModel.addComponent(components[i], targetContainer, -1); 565 } 566 addingInts = layoutModel.createIntervalsFromBounds(dragger.getMovingSpace(), components, dragger.getMovingBounds()); 567 break; 568 } else { 569 if (origSpace == null) { 570 origSpace = new LayoutRegion(); 571 for (int i=0; i < components.length; i++) { 573 origSpace.expand(components[i].getLayoutInterval(0).getCurrentSpace()); 574 } 575 } 576 LayoutInterval[] children = new LayoutInterval[components.length]; 577 for (int i=0; i<components.length; i++) { 578 children[i] = components[i].getLayoutInterval(dim); 579 } 580 LayoutInterval parent = LayoutInterval.getCommonParent(children); 581 addingInts[dim] = restrictedCopy(parent, components, origSpace, dim, null); 584 } 585 } else { 586 addingInts[dim] = components[0].getLayoutInterval(dim); 587 if (newComponent) { Dimension preferred = visualMapper.getComponentPreferredSize(components[0].getId()); 589 int size = dragger.getMovingBounds()[0].size(dim); 590 if (size != ((dim == HORIZONTAL) ? preferred.width : preferred.height)) { 591 LayoutInterval intr = addingInts[dim]; 592 layoutModel.setIntervalSize(intr, intr.getMinimumSize(), size, intr.getMaximumSize()); 593 } 594 } 595 } 596 } 597 598 LayoutFeeder layoutFeeder = new LayoutFeeder(operations, dragger, addingInts); 599 600 for (int i=0; i<components.length; i++) { 602 if (components[i].getParent() != null) { 603 LayoutComponent comp = components[i]; 604 if (components[i].getParent() != null) { 605 if (dragger.isResizing(HORIZONTAL)) { 606 layoutModel.removeComponentFromLinkSizedGroup(components[i], HORIZONTAL); 607 } 608 if (dragger.isResizing(VERTICAL)) { 609 layoutModel.removeComponentFromLinkSizedGroup(components[i], VERTICAL); 610 } 611 } 612 if (components.length == 1) { layoutModel.removeComponentAndIntervals(comp, false); 614 } 615 else { layoutModel.removeComponent(comp, false); 619 } 620 } 621 } 622 623 modelListener.deactivate(); 625 for (int i=0; i<components.length; i++) { 627 layoutModel.addComponent(components[i], targetContainer, -1); 628 } 629 630 layoutFeeder.add(); 632 if (layoutFeeder.imposeSize) 633 imposeSize = true; 634 if (layoutFeeder.optimizeStructure) 635 optimizeStructure = true; 636 641 for (int dim=0; dim < DIM_COUNT; dim++) { 642 destroyGroupIfRedundant(addingInts[dim], addingInts[dim].getParent()); 643 } 644 645 if (components[0].isLayoutContainer() && (dragger.isResizing() || newComponent)) { 646 imposeCurrentContainerSize(components[0], dragger.getSizes(), true); 648 } 649 else if (dragger.isResizing() && !components[0].isLayoutContainer()) { 650 for (int i=0; i < DIM_COUNT; i++) { 652 if (dragger.snappedToDefaultSize(i)) 653 operations.resizeInterval(components[0].getLayoutInterval(i), NOT_EXPLICITLY_DEFINED); 654 } 655 } 656 657 updateDesignModifications(targetContainer); 658 } 659 else { assert dragger.isResizing(); 661 662 modelListener.deactivate(); 664 LayoutRegion space = dragger.getMovingBounds()[0]; 665 for (int dim=0; dim < DIM_COUNT; dim++) { 666 components[0].getLayoutInterval(dim).setCurrentSpace(space); 667 } 668 if (components[0].isLayoutContainer()) { 669 imposeCurrentContainerSize(components[0], dragger.getSizes(), true); 670 } 671 } 672 673 if (dragger.isResizing() && components[0].isLayoutContainer()) 674 updateDesignModifications(components[0]); 675 676 visualStateUpToDate = false; 677 } 678 } finally { 679 modelListener.activate(); 680 dragger = null; 681 if (logTestCode()) { 682 testCode.add("ld.endMoving(" + committed + ");"); testCode.add("// < END MOVING"); } 685 } 686 } 687 688 699 private LayoutInterval restrictedCopy(LayoutInterval interval, 700 LayoutComponent[] components, LayoutRegion space, int dimension, List temp) { 701 boolean processTemp = (temp == null); 702 if (temp == null) { 703 temp = new LinkedList(); 704 } 705 if (interval.isGroup()) { 706 boolean parallel = interval.isParallel(); 707 LayoutInterval copy = new LayoutInterval(parallel ? PARALLEL : SEQUENTIAL); 708 copy.setAlignment(interval.getAlignment()); 709 copy.setAttributes(interval.getAttributes()); 710 copy.setSizes(interval.getMinimumSize(), interval.getPreferredSize(), interval.getMaximumSize()); 711 if (parallel) { 712 copy.setGroupAlignment(interval.getGroupAlignment()); 713 } 714 Iterator iter = interval.getSubIntervals(); 715 int compCount = 0; boolean includeGap = false; int firstGapToInclude = 0; int gapStart = interval.getCurrentSpace().positions[dimension][LEADING]; 719 while (iter.hasNext()) { 720 LayoutInterval sub = (LayoutInterval)iter.next(); 721 LayoutInterval subCopy = restrictedCopy(sub, components, space, dimension, temp); 722 if (subCopy != null) { 723 if (!sub.isEmptySpace()) { 724 if (includeGap) { 725 gapStart = Math.max(space.positions[dimension][LEADING], gapStart); 726 int size = sub.getCurrentSpace().positions[dimension][LEADING] - gapStart; 727 integrateGap(copy, size, firstGapToInclude); 728 includeGap = false; 729 } 730 gapStart = sub.getCurrentSpace().positions[dimension][TRAILING]; 731 firstGapToInclude = copy.getSubIntervalCount(); 732 } 733 if (sub.isComponent()) { 734 temp.add(subCopy); 739 temp.add(copy); 740 temp.add(new Integer (subCopy.getRawAlignment())); 741 temp.add(new Integer (copy.getSubIntervalCount() + compCount)); 742 compCount++; 743 } else { 744 layoutModel.addInterval(subCopy, copy, -1); 745 } 746 } else { 747 if (!parallel) { 748 includeGap = true; 749 } 750 } 751 } 752 if (includeGap) { 753 gapStart = Math.max(space.positions[dimension][LEADING], gapStart); 754 int gapEnd = Math.min(space.positions[dimension][TRAILING], interval.getCurrentSpace().positions[dimension][TRAILING]); 755 integrateGap(copy, gapEnd - gapStart, firstGapToInclude); 756 } 757 if (copy.getSubIntervalCount() + compCount > 0) { 758 if (processTemp) { 759 iter = temp.iterator(); 761 while (iter.hasNext()) { 762 LayoutInterval comp = (LayoutInterval)iter.next(); 763 LayoutInterval parent = (LayoutInterval)iter.next(); 764 int alignment = ((Integer )iter.next()).intValue(); 765 int index = ((Integer )iter.next()).intValue(); 766 layoutModel.removeInterval(comp); 767 layoutModel.setIntervalAlignment(comp, alignment); 768 layoutModel.addInterval(comp, parent, index); 769 } 770 boolean active = modelListener.isActive(); 772 if (active) modelListener.deactivate(); 773 iter = temp.iterator(); 774 while (iter.hasNext()) { 775 iter.next(); LayoutInterval group = (LayoutInterval)iter.next(); 777 iter.next(); iter.next(); while (group.getSubIntervalCount() == 1) { 779 LayoutInterval sub = group.getSubInterval(0); 780 LayoutInterval parent = group.getParent(); 781 layoutModel.removeInterval(sub); 782 int alignment = group.getAlignment(); 783 int index = layoutModel.removeInterval(group); 784 layoutModel.setIntervalAlignment(sub, alignment); 785 layoutModel.addInterval(sub, parent, index); 786 group = sub; 787 } 788 } 789 compCount = 0; 790 if (active) modelListener.activate(); 791 } 792 if ((copy.getSubIntervalCount() == 1) && (compCount == 0)) { 794 boolean active = modelListener.isActive(); 795 if (active) modelListener.deactivate(); 796 LayoutInterval subCopy = copy.getSubInterval(0); 797 layoutModel.removeInterval(subCopy); 798 layoutModel.setIntervalAlignment(subCopy, copy.getAlignment()); 799 if (copy.isSequential() && subCopy.isEmptySpace()) { 800 copy = null; 801 } else { 802 copy = subCopy; 803 } 804 if (active) modelListener.activate(); 805 } 806 return copy; 807 } else { 808 return null; 809 } 810 } else if (interval.isComponent()) { 811 LayoutComponent comp = interval.getComponent(); 812 if (Arrays.asList(components).contains(comp)) { 813 return interval; } 815 return null; 816 } else { 817 assert interval.isEmptySpace(); 818 int[] bounds = emptySpaceBounds(interval, dimension); 819 int rangeStart = space.positions[dimension][LEADING]; 820 int rangeEnd = space.positions[dimension][TRAILING]; 821 if ((bounds[0] < rangeEnd) && (bounds[1] > rangeStart)) { 822 LayoutInterval gap = new LayoutInterval(SINGLE); 823 gap.setAttributes(interval.getAttributes()); 824 if ((bounds[0] < rangeStart) || (bounds[1] > rangeEnd)) { 825 int min = interval.getMinimumSize(); 827 if (min >= 0) min = USE_PREFERRED_SIZE; 828 int pref = Math.min(bounds[1], rangeEnd) - Math.max(bounds[0], rangeStart); 829 int max = interval.getMaximumSize(); 830 if (max >= 0) max = USE_PREFERRED_SIZE; 831 gap.setSizes(min, pref, max); 832 } else { 833 gap.setSizes(interval.getMinimumSize(), interval.getPreferredSize(), interval.getMaximumSize()); 834 } 835 return gap; 836 } else { 837 return null; 839 } 840 } 841 } 842 843 854 private void integrateGap(LayoutInterval seqGroup, int size, int boundary) { 855 while ((seqGroup.getSubIntervalCount() > boundary) 856 && seqGroup.getSubInterval(seqGroup.getSubIntervalCount()-1).isEmptySpace()) { 857 layoutModel.removeInterval(seqGroup.getSubInterval(seqGroup.getSubIntervalCount()-1)); 858 } 859 if (size > 0) { 860 LayoutInterval gap = new LayoutInterval(SINGLE); 861 gap.setSize(size); 862 layoutModel.addInterval(gap, seqGroup, -1); 863 } 864 } 865 866 874 private int[] emptySpaceBounds(LayoutInterval emptySpace, int dimension) { 875 assert emptySpace.isEmptySpace(); 876 int leading, trailing; 877 LayoutInterval parent = emptySpace.getParent(); 878 int index = parent.indexOf(emptySpace); 879 if (index == 0) { 880 leading = parent.getCurrentSpace().positions[dimension][LEADING]; 881 } else { 882 leading = parent.getSubInterval(index - 1).getCurrentSpace().positions[dimension][TRAILING]; 883 } 884 if (index+1 == parent.getSubIntervalCount()) { 885 trailing = parent.getCurrentSpace().positions[dimension][TRAILING]; 886 } else { 887 trailing = parent.getSubInterval(index + 1).getCurrentSpace().positions[dimension][LEADING]; 888 } 889 return new int[] {leading, trailing}; 890 } 891 892 897 public void removeDraggedComponents() { 898 if (dragger != null) { 899 LayoutComponent[] components = dragger.getMovingComponents(); 900 for (int i=0; i < components.length; i++) { 901 layoutModel.removeComponentAndIntervals(components[i], !components[i].isLayoutContainer()); 902 } 903 endMoving(false); 904 } 905 } 906 907 public void paintMoveFeedback(Graphics2D g) { 908 if (dragger != null) { dragger.paintMoveFeedback(g); 910 } 911 } 912 913 919 public void paintSelection(Graphics2D g, String componentId) { 920 LayoutComponent comp = layoutModel.getLayoutComponent(componentId); 921 if ((comp != null) && (comp.getParent() != null)) { 922 paintSelection(g, comp, HORIZONTAL); 923 paintSelection(g, comp, VERTICAL); 924 } 925 } 926 927 935 private void paintSelection(Graphics2D g, LayoutComponent component, int dimension) { 936 LayoutInterval interval = component.getLayoutInterval(dimension); 937 if (component.isLinkSized(HORIZONTAL) || component.isLinkSized(VERTICAL)) { 938 paintLinks(g, component); 939 } 940 if (interval.getAlignment() == BASELINE) { 942 LayoutInterval alignedParent = interval.getParent(); 943 int oppDimension = (dimension == HORIZONTAL) ? VERTICAL : HORIZONTAL; 944 LayoutRegion region = alignedParent.getCurrentSpace(); 945 int x = region.positions[dimension][BASELINE]; 946 int y1 = region.positions[oppDimension][LEADING]; 947 int y2 = region.positions[oppDimension][TRAILING]; 948 if ((y1 != LayoutRegion.UNKNOWN) && (y2 != LayoutRegion.UNKNOWN)) { 949 if (dimension == HORIZONTAL) { 950 g.drawLine(x, y1, x, y2); 951 } else { 952 g.drawLine(y1, x, y2, x); 953 } 954 } 955 } 956 int lastAlignment = -1; 957 while (interval.getParent() != null) { 958 LayoutInterval parent = interval.getParent(); 959 if (parent.getType() == SEQUENTIAL) { 960 int alignment = LayoutInterval.getEffectiveAlignment(interval); 961 int index = parent.indexOf(interval); 962 int start, end; 963 switch (alignment) { 964 case LEADING: 965 start = 0; 966 end = index; 967 lastAlignment = LEADING; 968 break; 969 case TRAILING: 970 start = index + 1; 971 end = parent.getSubIntervalCount(); 972 lastAlignment = TRAILING; 973 break; 974 default: switch (lastAlignment) { 975 case LEADING: start = 0; end = index; break; 976 case TRAILING: start = index+1; end = parent.getSubIntervalCount(); break; 977 default: start = 0; end = parent.getSubIntervalCount(); break; 978 } 979 } 980 for (int i=start; i<end; i++) { 981 LayoutInterval candidate = parent.getSubInterval(i); 982 if (candidate.isEmptySpace()) { 983 paintAlignment(g, candidate, dimension, LayoutInterval.getEffectiveAlignment(candidate)); 984 } 985 } 986 } else { 987 int alignment = interval.getAlignment(); 988 if (!LayoutInterval.wantResizeInLayout(interval)) { 989 lastAlignment = alignment; 990 } 991 paintAlignment(g, interval, dimension, lastAlignment); 992 } 993 interval = interval.getParent(); 994 } 995 } 996 997 private void paintLinks(Graphics2D g, LayoutComponent component) { 998 999 if ((component.isLinkSized(HORIZONTAL)) && (component.isLinkSized(VERTICAL))) { 1000 Map linkGroupsH = layoutModel.getLinkSizeGroups(HORIZONTAL); 1001 Map linkGroupsV = layoutModel.getLinkSizeGroups(VERTICAL); 1002 Integer linkIdH = new Integer (component.getLinkSizeId(HORIZONTAL)); 1003 Integer linkIdV = new Integer (component.getLinkSizeId(VERTICAL)); 1004 1005 List lH = (List)linkGroupsH.get(linkIdH); 1006 List lV = (List)linkGroupsV.get(linkIdV); 1007 1008 Set merged = new HashSet(); 1009 for (int i=0; i < lH.size(); i++) { 1010 merged.add(lH.get(i)); 1011 } 1012 for (int i=0; i < lV.size(); i++) { 1013 merged.add(lV.get(i)); 1014 } 1015 1016 Iterator mergedIt = merged.iterator(); 1017 while (mergedIt.hasNext()) { 1018 String id = (String )mergedIt.next(); 1019 LayoutComponent lc = layoutModel.getLayoutComponent(id); 1020 LayoutInterval interval = lc.getLayoutInterval(HORIZONTAL); 1021 LayoutRegion region = interval.getCurrentSpace(); 1022 Image badge = null; 1023 if ((lV.contains(id)) && (lH.contains(id))) { 1024 badge = getLinkBadge(BOTH_DIMENSIONS); 1025 } else { 1026 if (lH.contains(lc.getId())) { 1027 badge = getLinkBadge(HORIZONTAL); 1028 } 1029 if (lV.contains(lc.getId())) { 1030 badge = getLinkBadge(VERTICAL); 1031 } 1032 } 1033 int x = region.positions[HORIZONTAL][TRAILING] - region.size(HORIZONTAL) / 4 - (badge.getWidth(null) / 2); 1034 int y = region.positions[VERTICAL][LEADING] - (badge.getHeight(null)); 1035 g.drawImage(badge, x, y, null); 1036 } 1037 } else { 1038 int dimension = (component.isLinkSized(HORIZONTAL)) ? HORIZONTAL : VERTICAL; 1039 Map map = layoutModel.getLinkSizeGroups(dimension); 1040 1041 Integer linkId = new Integer (component.getLinkSizeId(dimension)); 1042 List l = (List)map.get(linkId); 1043 Iterator mergedIt = l.iterator(); 1044 1045 while (mergedIt.hasNext()) { 1046 String id = (String )mergedIt.next(); 1047 LayoutComponent lc = layoutModel.getLayoutComponent(id); 1048 LayoutInterval interval = lc.getLayoutInterval(dimension); 1049 LayoutRegion region = interval.getCurrentSpace(); 1050 Image badge = getLinkBadge(dimension); 1051 int x = region.positions[HORIZONTAL][TRAILING] - region.size(HORIZONTAL) / 4 - (badge.getWidth(null) / 2); 1052 int y = region.positions[VERTICAL][LEADING] - (badge.getHeight(null)); 1053 g.drawImage(badge, x, y, null); 1054 } 1055 } 1056 } 1057 1058 private Image linkBadgeBoth = null; 1059 private Image linkBadgeHorizontal = null; 1060 private Image linkBadgeVertical = null; 1061 1062 private static final int BOTH_DIMENSIONS = 2; 1063 1064 private Image getLinkBadge(int dimension) { 1065 if (dimension == (BOTH_DIMENSIONS)) { 1066 if (linkBadgeBoth == null) { 1067 linkBadgeBoth = Utilities.loadImage("org/netbeans/modules/form/resources/sameboth.png"); } 1069 return linkBadgeBoth; 1070 } 1071 if (dimension == HORIZONTAL) { 1072 if (linkBadgeHorizontal == null) { 1073 linkBadgeHorizontal = Utilities.loadImage("org/netbeans/modules/form/resources/samewidth.png"); } 1075 return linkBadgeHorizontal; 1076 } 1077 if (dimension == VERTICAL) { 1078 if (linkBadgeVertical == null) { 1079 linkBadgeVertical = Utilities.loadImage("org/netbeans/modules/form/resources/sameheight.png"); } 1081 return linkBadgeVertical; 1082 } 1083 return null; 1084 } 1085 1086 private void paintAlignment(Graphics2D g, LayoutInterval interval, int dimension, int alignment) { 1087 LayoutInterval parent = interval.getParent(); 1088 boolean baseline = parent.isParallel() && (parent.getGroupAlignment() == BASELINE); 1089 LayoutRegion group = parent.getCurrentSpace(); 1090 int opposite = (dimension == HORIZONTAL) ? VERTICAL : HORIZONTAL; 1091 int x1, x2, y; 1092 if (interval.isEmptySpace()) { 1093 int index = parent.indexOf(interval); 1094 int[] ya, yb; 1095 boolean x1group, x2group; 1096 if (index == 0) { 1097 x1 = group.positions[dimension][baseline ? BASELINE : LEADING]; 1098 ya = visualIntervalPosition(parent, opposite, LEADING); 1099 x1group = LayoutInterval.getFirstParent(interval, PARALLEL).getParent() != null; 1100 } else { 1101 LayoutInterval x1int = parent.getSubInterval(index-1); 1102 if (x1int.isParallel() && (x1int.getGroupAlignment() == BASELINE)) { 1103 x1 = x1int.getCurrentSpace().positions[dimension][BASELINE]; 1104 } else { 1105 if (x1int.isEmptySpace()) return; 1106 x1 = x1int.getCurrentSpace().positions[dimension][TRAILING]; 1107 } 1108 ya = visualIntervalPosition(x1int, opposite, TRAILING); 1109 x1group = x1int.isGroup(); 1110 } 1111 if (index + 1 == parent.getSubIntervalCount()) { 1112 x2 = group.positions[dimension][baseline ? BASELINE : TRAILING]; 1113 yb = visualIntervalPosition(parent, opposite, TRAILING); 1114 x2group = LayoutInterval.getFirstParent(interval, PARALLEL).getParent() != null; 1115 } else { 1116 LayoutInterval x2int = parent.getSubInterval(index+1); 1117 if (x2int.isParallel() && (x2int.getGroupAlignment() == BASELINE)) { 1118 x2 = x2int.getCurrentSpace().positions[dimension][BASELINE]; 1119 } else { 1120 if (x2int.isEmptySpace()) return; 1121 x2 = x2int.getCurrentSpace().positions[dimension][LEADING]; 1122 } 1123 yb = visualIntervalPosition(x2int, opposite, LEADING); 1124 x2group = x2int.isGroup(); 1125 } 1126 if ((x1 == LayoutRegion.UNKNOWN) || (x2 == LayoutRegion.UNKNOWN)) return; 1127 int y1 = Math.min(ya[1], yb[1]); 1128 int y2 = Math.max(ya[0], yb[0]); 1129 y = (y1 + y2)/2; 1130 if ((ya[1] < yb[0]) || (yb[1] < ya[0])) { 1131 if (dimension == HORIZONTAL) { 1133 g.drawLine(x1, ya[0], x1, y); 1134 g.drawLine(x1, ya[0], x1, ya[1]); 1135 g.drawLine(x2, yb[0], x2, y); 1136 g.drawLine(x2, yb[0], x2, yb[1]); 1137 } else { 1138 g.drawLine(ya[0], x1, y, x1); 1139 g.drawLine(ya[0], x1, ya[1], x1); 1140 g.drawLine(yb[0], x2, y, x2); 1141 g.drawLine(yb[0], x2, yb[1], x2); 1142 } 1143 } else { 1144 if (dimension == HORIZONTAL) { 1145 if (x1group) g.drawLine(x1, ya[0], x1, ya[1]); 1146 if (x2group) g.drawLine(x2, yb[0], x2, yb[1]); 1147 } else { 1148 if (x1group) g.drawLine(ya[0], x1, ya[1], x1); 1149 if (x2group) g.drawLine(yb[0], x2, yb[1], x2); 1150 } 1151 } 1152 } else { 1153 LayoutRegion child = interval.getCurrentSpace(); 1154 if ((alignment == LEADING) || (alignment == TRAILING)) { 1155 x1 = group.positions[dimension][baseline ? BASELINE : alignment]; 1156 if (interval.isParallel() && (interval.getAlignment() == BASELINE)) { 1157 x2 = child.positions[dimension][BASELINE]; 1158 } else { 1159 x2 = child.positions[dimension][alignment]; 1160 } 1161 } else { 1162 return; 1163 } 1164 if ((x1 == LayoutRegion.UNKNOWN) || (x2 == LayoutRegion.UNKNOWN)) return; 1165 int[] pos = visualIntervalPosition(parent, opposite, alignment); 1166 y = (pos[0] + pos[1])/2; 1167 int xa = group.positions[dimension][LEADING]; 1168 int xb = group.positions[dimension][TRAILING]; 1169 if (parent.getParent() != null) { 1170 if (dimension == HORIZONTAL) { 1171 if (alignment == LEADING) { 1172 g.drawLine(xa, pos[0], xa, pos[1]); 1173 } else if (alignment == TRAILING) { 1174 g.drawLine(xb, pos[0], xb, pos[1]); 1175 } 1176 } else { 1177 if (alignment == LEADING) { 1178 g.drawLine(pos[0], xa, pos[1], xa); 1179 } else if (alignment == TRAILING) { 1180 g.drawLine(pos[0], xb, pos[1], xb); 1181 } 1182 } 1183 } 1184 } 1185 if ((x2 - x1 > 1) && (Math.abs(y) <= Short.MAX_VALUE) 1187 && (Math.abs(x1) <= Short.MAX_VALUE) && (Math.abs(x2) <= Short.MAX_VALUE)) { 1188 int x, angle; 1189 if (alignment == LEADING) { 1190 x = x1; 1191 angle = 180; 1192 } else { 1193 x = x2; 1194 angle = 0; 1195 } 1196 x2--; 1197 int diam = Math.min(4, x2-x1); 1198 Stroke stroke = new BasicStroke (1, BasicStroke.CAP_BUTT, 1199 BasicStroke.JOIN_BEVEL, 0, new float[] {1, 1}, 0); 1200 Stroke oldStroke = g.getStroke(); 1201 g.setStroke(stroke); 1202 if (dimension == HORIZONTAL) { 1203 g.drawLine(x1, y, x2, y); 1204 angle += 90; 1205 } else { 1206 g.drawLine(y, x1, y, x2); 1207 int temp = x; x = y; y = temp; 1208 } 1209 g.setStroke(oldStroke); 1210 if ((alignment == LEADING) || (alignment == TRAILING)) { 1211 Object hint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 1212 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 1213 g.fillArc(x-diam, y-diam, 2*diam, 2*diam, angle, 180); 1214 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint); 1215 } 1216 } 1217 } 1218 1219 private int[] visualIntervalPosition(LayoutInterval interval, int dimension, int alignment) { 1220 int min = Short.MAX_VALUE; 1221 int max = Short.MIN_VALUE; 1222 if (interval.isParallel() && (interval.getGroupAlignment() != BASELINE)) { 1223 Iterator iter = interval.getSubIntervals(); 1224 while (iter.hasNext()) { 1225 LayoutInterval subInterval = (LayoutInterval)iter.next(); 1226 int imin, imax; 1227 int oppDim = (dimension == HORIZONTAL) ? VERTICAL : HORIZONTAL; 1228 if (LayoutInterval.isPlacedAtBorder(subInterval, oppDim, alignment)) { 1229 if (subInterval.isParallel()) { 1230 int[] ipos = visualIntervalPosition(subInterval, dimension, alignment); 1231 imin = ipos[0]; imax = ipos[1]; 1232 } else if (!subInterval.isEmptySpace()) { 1233 LayoutRegion region = subInterval.getCurrentSpace(); 1234 imin = region.positions[dimension][LEADING]; 1235 imax = region.positions[dimension][TRAILING]; 1236 } else { 1237 imin = min; imax = max; 1238 } 1239 } else { 1240 imin = min; imax = max; 1241 } 1242 if (min > imin) min = imin; 1243 if (max < imax) max = imax; 1244 } 1245 } 1246 if (!interval.isParallel() || (min == Short.MAX_VALUE)) { 1247 LayoutRegion region = interval.getCurrentSpace(); 1248 min = region.positions[dimension][LEADING]; 1249 max = region.positions[dimension][TRAILING]; 1250 } 1251 return new int[] {min, max}; 1252 } 1253 1254 1257 class Listener implements LayoutModel.Listener { 1258 private boolean active = false; 1259 1260 public void layoutChanged(LayoutEvent ev) { 1261 if (!layoutModel.isUndoRedoInProgress()) { 1262 deactivate(); 1263 LayoutDesigner.this.layoutChanged(ev); 1264 activate(); 1265 } 1266 } 1267 void activate() { 1268 layoutModel.addListener(this); 1269 active = true; 1270 } 1271 void deactivate() { 1272 layoutModel.removeListener(this); 1273 active = false; 1274 } 1275 1276 boolean isActive() { 1277 return active; 1278 } 1279 }; 1280 1281 private void layoutChanged(LayoutEvent ev) { 1282 if (ev.getType() == LayoutEvent.INTERVAL_REMOVED) { 1283 LayoutInterval interval = ev.getInterval(); 1285 LayoutComponent comp = interval.getComponent(); 1286 if (comp != null) { 1287 int dim = -1; 1288 for (int i=0; i < DIM_COUNT; i++) { 1289 if (comp.getLayoutInterval(i) == interval) { 1290 dim = i; 1291 break; 1292 } 1293 } 1294 assert dim > -1; 1295 intervalRemoved(ev.getParentInterval(), 1296 ev.getIndex(), 1297 true, 1298 LayoutInterval.wantResize(ev.getInterval()), 1299 dim); 1300 if (comp.getParent() != null) { 1301 updateDesignModifications(comp.getParent().getLayoutRoot(dim), dim); 1302 visualStateUpToDate = false; 1305 } 1306 } 1307 } 1308 } 1309 1310 1312 private boolean isComponentResizable(LayoutComponent comp, int dimension) { 1313 boolean[] res = comp.getResizability(); 1314 if (res == null) { 1315 res = visualMapper.getComponentResizability(comp.getId(), new boolean[DIM_COUNT]); 1316 comp.setResizability(res); 1317 } 1318 return res[dimension]; 1319 } 1320 1321 1328 public void adjustComponentAlignment(LayoutComponent comp, int dimension, int alignment) { 1329 if (logTestCode()) { 1330 testCode.add("// > ADJUST COMPONENT ALIGNMENT"); testCode.add("{"); testCode.add("LayoutComponent comp = model.getLayoutComponent(\"" + comp.getId() + "\");"); testCode.add("int dimension = " + dimension); testCode.add("int alignment = " + alignment); testCode.add("ld.adjustComponentAlignment(comp, dimension, alignment);"); testCode.add("}"); } 1338 modelListener.deactivate(); 1339 LayoutInterval interval = comp.getLayoutInterval(dimension); 1340 1341 LayoutInterval parent = interval.getParent(); 1343 while (parent != null) { 1344 if (!LayoutInterval.canResize(parent)) { 1345 interval = parent; 1346 } 1347 parent = parent.getParent(); 1348 } 1349 assert !LayoutInterval.wantResize(interval); 1350 1351 boolean changed = false; 1352 parent = interval.getParent(); 1353 while (parent != null) { 1354 if (parent.isParallel()) { 1355 if (LayoutInterval.wantResize(parent) && !LayoutInterval.wantResize(interval)) { 1356 int alg = interval.getAlignment(); 1357 if (alg != alignment) { 1358 int size = LayoutInterval.getIntervalCurrentSize(parent, dimension) 1360 - LayoutInterval.getIntervalCurrentSize(interval, dimension); 1361 if (size > 0) { 1362 if (!interval.isSequential()) { 1363 LayoutInterval seq = new LayoutInterval(SEQUENTIAL); 1364 layoutModel.setIntervalAlignment(interval, DEFAULT); 1365 int i = layoutModel.removeInterval(interval); 1366 layoutModel.addInterval(interval, seq, -1); 1367 layoutModel.addInterval(seq, parent, i); 1368 interval = seq; 1369 } 1370 int index = (alg == LEADING) ? -1 : 0; 1371 LayoutInterval gap = new LayoutInterval(SINGLE); 1372 gap.setSize(size); 1373 layoutModel.addInterval(gap, interval, index); 1374 } 1375 layoutModel.setIntervalAlignment(interval, alignment); 1376 } 1377 changed = true; 1378 } 1379 } else { 1380 boolean before = true; 1381 boolean seqChanged = false; 1382 for (int i=0; i<parent.getSubIntervalCount(); i++) { 1383 LayoutInterval li = parent.getSubInterval(i); 1384 if (li == interval) { 1385 before = false; 1386 } else if (LayoutInterval.wantResize(li)) { 1387 if ((before && (alignment == LEADING)) || (!before && (alignment == TRAILING))) { 1388 assert li.isEmptySpace(); 1389 setIntervalResizing(li, false); 1390 if (li.getPreferredSize() == 0) { 1391 layoutModel.removeInterval(li); 1392 i--; 1393 } 1394 seqChanged = true; 1395 } 1396 } 1397 } 1398 if (!changed && seqChanged) { 1399 boolean insertGap = false; 1400 int index = parent.indexOf(interval); 1401 if (alignment == LEADING) { 1402 if (parent.getSubIntervalCount() <= index+1) { 1403 insertGap = true; 1404 index = -1; 1405 } else { 1406 index++; 1407 LayoutInterval candidate = parent.getSubInterval(index); 1408 if (candidate.isEmptySpace()) { 1409 setIntervalResizing(candidate, true); 1410 } else { 1411 insertGap = true; 1412 } 1413 } 1414 } else { 1415 assert (alignment == TRAILING); 1416 if (index == 0) { 1417 insertGap = true; 1418 } else { 1419 LayoutInterval candidate = parent.getSubInterval(index-1); 1420 if (candidate.isEmptySpace()) { 1421 setIntervalResizing(candidate, true); 1422 } else { 1423 insertGap = true; 1424 } 1425 } 1426 } 1427 if (insertGap) { 1428 LayoutInterval gap = new LayoutInterval(SINGLE); 1429 setIntervalResizing(gap, true); 1430 layoutModel.setIntervalSize(gap, 0, 0, gap.getMaximumSize()); 1431 layoutModel.addInterval(gap, parent, index); 1432 } 1433 changed = true; 1434 } 1435 } 1436 interval = parent; 1437 parent = parent.getParent(); 1438 } 1439 updateDesignModifications(interval, dimension); 1440 modelListener.activate(); 1441 visualStateUpToDate = false; 1442 if (logTestCode()) { 1443 testCode.add("// < ADJUST COMPONENT ALIGNMENT"); } 1445 } 1446 1447 1460 public int[] getAdjustableComponentAlignment(LayoutComponent comp, int dimension) { 1461 LayoutInterval interval = comp.getLayoutInterval(dimension); 1462 boolean leadingFixed = true; 1463 boolean trailingFixed = true; 1464 boolean leadingAdjustable = true; 1465 boolean trailingAdjustable = true; 1466 1467 if (LayoutInterval.wantResize(interval)) { 1468 leadingFixed = trailingFixed = leadingAdjustable = trailingAdjustable = false; 1469 } 1470 LayoutInterval parent = interval.getParent(); 1471 while (parent != null) { 1472 if (!LayoutInterval.canResize(parent)) { 1473 leadingFixed = trailingFixed = leadingAdjustable = trailingAdjustable = true; 1474 } else if (parent.isParallel()) { 1475 if (LayoutInterval.wantResize(parent) && !LayoutInterval.wantResize(interval)) { 1476 int alignment = interval.getAlignment(); 1477 if (alignment == LEADING) { 1478 trailingFixed = false; 1479 } else if (alignment == TRAILING) { 1480 leadingFixed = false; 1481 } 1482 } 1483 } else { 1484 boolean before = true; 1485 Iterator iter = parent.getSubIntervals(); 1486 while (iter.hasNext()) { 1487 LayoutInterval li = (LayoutInterval)iter.next(); 1488 if (li == interval) { 1489 before = false; 1490 } else if (LayoutInterval.wantResize(li)) { 1491 boolean space = li.isEmptySpace(); 1492 if (before) { 1493 leadingFixed = false; 1494 if (!space) { 1495 leadingAdjustable = false; 1496 } 1497 } else { 1498 trailingFixed = false; 1499 if (!space) { 1500 trailingAdjustable = false; 1501 } 1502 } 1503 } 1504 } 1505 } 1506 interval = parent; 1507 parent = parent.getParent(); 1508 } 1509 int adjustable = (leadingAdjustable ? 1 << LEADING : 0) + (trailingAdjustable ? 1 << TRAILING : 0); 1510 if (leadingFixed && trailingFixed) { 1511 if (LEADING == interval.getGroupAlignment()) { 1513 trailingFixed = false; 1514 } else { 1515 leadingFixed = false; 1516 } 1517 } 1518 int alignment; 1519 if (leadingFixed) { 1520 alignment = LEADING; 1522 } else { 1523 if (trailingFixed) { 1524 alignment = TRAILING; 1525 } else { 1526 alignment = -1; 1527 } 1528 } 1529 return new int[] {alignment, adjustable}; 1530 } 1531 1532 1540 public boolean isComponentResizing(LayoutComponent comp, int dimension) { 1541 LayoutInterval interval = comp.getLayoutInterval(dimension); 1542 boolean fill = interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL); 1543 return fill ? false : LayoutInterval.wantResizeInLayout(interval); 1544 } 1545 1546 1552 private int prefSizeOfInterval(LayoutInterval interval) { 1553 int dimension = -1; 1554 if (interval.isComponent()) { 1555 LayoutComponent comp = interval.getComponent(); 1556 dimension = (interval == comp.getLayoutInterval(HORIZONTAL)) ? HORIZONTAL : VERTICAL; 1557 if (comp.isLinkSized(dimension)) { 1558 Collection linked = (Collection)layoutModel.getLinkSizeGroups(dimension).get(new Integer (comp.getLinkSizeId(dimension))); 1559 Iterator iter = linked.iterator(); 1560 int prefSize = 0; 1561 while (iter.hasNext()) { 1562 String compId = (String )iter.next(); 1563 LayoutComponent component = layoutModel.getLayoutComponent(compId); 1564 LayoutInterval intr = component.getLayoutInterval(dimension); 1565 int pref = intr.getPreferredSize(); 1566 if (pref == NOT_EXPLICITLY_DEFINED) { 1567 Dimension prefDim = visualMapper.getComponentPreferredSize(compId); 1568 pref = (dimension == HORIZONTAL) ? prefDim.width : prefDim.height; 1569 } 1570 prefSize = Math.max(pref, prefSize); 1571 } 1572 return prefSize; 1573 } 1574 } 1575 int prefSize = interval.getPreferredSize(); 1576 if (prefSize == NOT_EXPLICITLY_DEFINED) { 1577 if (interval.isComponent()) { 1578 LayoutComponent comp = interval.getComponent(); 1579 Dimension pref = visualMapper.getComponentPreferredSize(comp.getId()); 1580 return (dimension == HORIZONTAL) ? pref.width : pref.height; 1581 } else if (interval.isEmptySpace()) { 1582 return sizeOfEmptySpace(interval); 1583 } else { 1584 assert interval.isGroup(); 1585 prefSize = 0; 1586 Iterator iter = interval.getSubIntervals(); 1587 if (interval.isSequential()) { 1588 while (iter.hasNext()) { 1589 LayoutInterval subInterval = (LayoutInterval)iter.next(); 1590 prefSize += prefSizeOfInterval(subInterval); 1591 } 1592 } else { 1593 while (iter.hasNext()) { 1594 LayoutInterval subInterval = (LayoutInterval)iter.next(); 1595 prefSize = Math.max(prefSize, prefSizeOfInterval(subInterval)); 1596 } 1597 } 1598 } 1599 } 1600 return prefSize; 1601 } 1602 1603 1609 private int sizeOfEmptySpace(LayoutInterval interval) { 1610 return LayoutUtils.getSizeOfDefaultGap(interval, visualMapper); 1611 } 1612 1613 1621 public void setComponentResizing(LayoutComponent comp, int dimension, boolean resizing) { 1622 if (logTestCode()) { 1623 testCode.add("// > SET COMPONENT RESIZING"); testCode.add("{"); testCode.add("LayoutComponent comp = lm.getLayoutComponent(\"" + comp.getId() + "\");"); testCode.add("int dimension = " + dimension + ";"); testCode.add("boolean resizing = " + resizing + ";"); testCode.add("ld.setComponentResizing(comp, dimension, resizing);"); testCode.add("}"); } 1631 modelListener.deactivate(); 1632 LayoutInterval interval = comp.getLayoutInterval(dimension); 1633 1634 if (resizing && comp.isLinkSized(dimension)) { 1636 Collection linked = (Collection)layoutModel.getLinkSizeGroups(dimension).get(new Integer (comp.getLinkSizeId(dimension))); 1637 Collection toChange; 1638 if (linked.size() == 2) { toChange = linked; 1640 } else { 1641 toChange = Collections.singletonList(comp.getId()); 1642 } 1643 Iterator iter = toChange.iterator(); 1644 while (iter.hasNext()) { 1645 String compId = (String )iter.next(); 1646 LayoutComponent component = layoutModel.getLayoutComponent(compId); 1647 LayoutInterval intr = component.getLayoutInterval(dimension); 1648 Dimension prefDim = visualMapper.getComponentPreferredSize(compId); 1649 int prefSize = (dimension == HORIZONTAL) ? prefDim.width : prefDim.height; 1650 int currSize = intr.getCurrentSpace().size(dimension); 1651 if (currSize == prefSize) { 1652 currSize = NOT_EXPLICITLY_DEFINED; 1653 } 1654 layoutModel.setIntervalSize(intr, intr.getMinimumSize(), currSize, intr.getMaximumSize()); 1655 } 1656 } 1657 1658 LayoutInterval parent = interval.getParent(); 1659 boolean fill = interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL); 1660 boolean formerFill = interval.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL); 1661 if (fill || formerFill) { 1662 switchFillAttribute(interval, resizing); 1663 } else { 1664 setIntervalResizing(interval, resizing); 1665 } 1666 int delta = 0; 1667 if (!resizing) { 1668 int currSize = LayoutInterval.getIntervalCurrentSize(interval, dimension); 1669 int prefSize = prefSizeOfInterval(interval); 1670 delta = currSize - prefSize; 1671 if (delta != 0) { 1672 layoutModel.setIntervalSize(interval, interval.getMinimumSize(), currSize, interval.getMaximumSize()); 1673 } 1674 } 1675 LayoutInterval intr = interval; 1676 LayoutInterval par = parent; 1677 while (par != null) { 1678 if (par.isParallel() && resizing) { 1679 int groupCurrSize = LayoutInterval.getIntervalCurrentSize(par, dimension); 1680 int currSize = LayoutInterval.getIntervalCurrentSize(intr, dimension); 1681 if (groupCurrSize != currSize) { 1683 LayoutInterval seqGroup = intr; 1684 LayoutInterval space = new LayoutInterval(SINGLE); 1685 space.setSize(groupCurrSize - currSize); 1686 int alignment = intr.getAlignment(); 1687 int index = (alignment == LEADING) ? -1 : 0; 1688 if (intr.isSequential()) { 1689 int spaceIndex = (alignment == LEADING) ? intr.getSubIntervalCount()-1 : 0; 1690 LayoutInterval adjacentSpace = intr.getSubInterval(spaceIndex); 1691 if (adjacentSpace.isEmptySpace()) { 1692 int spaceSize = LayoutInterval.getIntervalCurrentSize(adjacentSpace, dimension); 1693 layoutModel.removeInterval(adjacentSpace); 1694 space.setSize(groupCurrSize - currSize + spaceSize); 1695 } 1696 } else { 1697 seqGroup = new LayoutInterval(SEQUENTIAL); 1698 layoutModel.setIntervalAlignment(intr, DEFAULT); 1699 seqGroup.setAlignment(alignment); 1700 int i = layoutModel.removeInterval(intr); 1701 layoutModel.addInterval(intr, seqGroup, -1); 1702 layoutModel.addInterval(seqGroup, par, i); 1703 } 1704 layoutModel.addInterval(space, seqGroup, index); 1705 seqGroup.getCurrentSpace().set(dimension, par.getCurrentSpace()); 1706 } 1707 } else if (par.isSequential()) { 1708 boolean parentSeq = (parent == par); 1710 List resizableList = new LinkedList(); 1711 int alignment = parentSeq ? LayoutInterval.getEffectiveAlignment(interval) : 0; 1712 LayoutInterval leadingGap = null; 1713 LayoutInterval trailingGap = null; 1714 boolean afterDefining = false; 1715 Iterator iter = par.getSubIntervals(); 1716 while (iter.hasNext()) { 1717 LayoutInterval candidate = (LayoutInterval)iter.next(); 1718 if (candidate == interval) { 1719 afterDefining = true; 1720 } 1721 if (candidate.isEmptySpace()) { 1722 if (resizing) { 1723 setIntervalResizing(candidate, false); 1724 int currSize = LayoutInterval.getIntervalCurrentSize(candidate, dimension); 1725 int prefSize = prefSizeOfInterval(candidate); 1726 if (currSize != prefSize) { 1727 layoutModel.setIntervalSize(candidate, candidate.getMinimumSize(), 1728 currSize, candidate.getMaximumSize()); 1729 delta += currSize - prefSize; 1730 } 1731 } else if (parentSeq) { 1732 boolean wasFill = candidate.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL); 1733 boolean glue = (candidate.getPreferredSize() != NOT_EXPLICITLY_DEFINED); 1734 if (wasFill) { 1735 trailingGap = candidate; 1736 } else if ((trailingGap == null) || (!trailingGap.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL))) { 1737 if (glue) { 1738 trailingGap = candidate; 1739 } else { 1740 if (afterDefining && ((trailingGap == null) || (trailingGap.getPreferredSize() == NOT_EXPLICITLY_DEFINED))) { 1741 trailingGap = candidate; 1742 } 1743 } 1744 } 1745 if ((leadingGap == null) && !afterDefining) { 1746 leadingGap = candidate; 1747 } else { 1748 if ((wasFill && ((leadingGap == null) || (!leadingGap.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL)))) 1749 || glue && ((leadingGap == null) || (!leadingGap.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL) 1750 && (leadingGap.getPreferredSize() == NOT_EXPLICITLY_DEFINED)))) { 1751 leadingGap = candidate; 1752 } 1753 } 1754 } 1755 } else { 1756 if (candidate.getMaximumSize() == Short.MAX_VALUE) { 1757 resizableList.add(candidate); 1758 } 1759 } 1760 } 1761 if (resizableList.size() > 0) { 1762 iter = resizableList.iterator(); 1763 delta = (LayoutInterval.getIntervalCurrentSize(par, dimension) - prefSizeOfInterval(par) + delta)/resizableList.size(); 1764 while (iter.hasNext()) { 1765 LayoutInterval candidate = (LayoutInterval)iter.next(); 1766 if (candidate.isGroup()) { 1767 } else { 1769 if (candidate == interval) { 1770 if (delta != 0) { 1771 int prefSize = prefSizeOfInterval(candidate); 1772 layoutModel.setIntervalSize(candidate, candidate.getMinimumSize(), 1773 Math.max(0, prefSize - delta), candidate.getMaximumSize()); 1774 } 1775 } else { 1776 int currSize = LayoutInterval.getIntervalCurrentSize(candidate, dimension); 1777 layoutModel.setIntervalSize(candidate, candidate.getMinimumSize(), 1778 Math.max(0, currSize - delta), candidate.getMaximumSize()); 1779 } 1780 } 1781 } 1782 } 1783 if (parentSeq) { 1784 if (!LayoutInterval.wantResize(par)) { 1785 LayoutInterval gap = null; 1786 if ((alignment == TRAILING) && (leadingGap != null)) { 1787 gap = leadingGap; 1788 setIntervalResizing(leadingGap, !resizing); 1789 layoutModel.changeIntervalAttribute(leadingGap, LayoutInterval.ATTRIBUTE_FILL, true); 1790 } 1791 if ((alignment == LEADING) && (trailingGap != null)) { 1792 gap = trailingGap; 1793 setIntervalResizing(trailingGap, !resizing); 1794 layoutModel.changeIntervalAttribute(trailingGap, LayoutInterval.ATTRIBUTE_FILL, true); 1795 } 1796 if ((gap != null) && (delta != 0) && (gap.getPreferredSize() != NOT_EXPLICITLY_DEFINED)) { 1797 layoutModel.setIntervalSize(gap, gap.getMinimumSize(), 1798 Math.max(0, gap.getPreferredSize() - delta), gap.getMaximumSize()); 1799 } 1800 } 1801 parent = par.getParent(); } 1803 } 1804 intr = par; 1805 par = par.getParent(); 1806 } 1807 1808 if (resizing) { 1810 layoutModel.unsetSameSize(Collections.singletonList(comp.getId()), dimension); 1811 } 1812 modelListener.activate(); 1813 1814 if (resizing) { 1815 while (parent != null) { 1817 if (!LayoutInterval.canResize(parent)) { 1818 operations.enableGroupResizing(parent); 1819 } 1820 parent = parent.getParent(); 1821 } 1822 } else { 1823 while (parent != null) { 1825 if (fillResizable(parent)) { 1826 operations.suppressGroupResizing(parent); 1827 parent = parent.getParent(); 1828 } else { 1829 break; 1830 } 1831 } 1832 } 1833 1834 updateDesignModifications(comp.getParent()); 1835 visualStateUpToDate = false; 1836 if (logTestCode()) { 1837 testCode.add("// < SET COMPONENT RESIZING"); } 1839 } 1840 1841 private boolean fillResizable(LayoutInterval interval) { 1842 if (!LayoutInterval.canResize(interval)) { 1843 return false; 1844 } 1845 if (interval.isGroup()) { 1846 boolean subres = true; 1847 Iterator it = interval.getSubIntervals(); 1848 while (it.hasNext()) { 1849 LayoutInterval li = (LayoutInterval)it.next(); 1850 if (LayoutInterval.wantResize(li) && !fillResizable(li)) { 1851 subres = false; 1852 break; 1853 } 1854 } 1855 return subres; 1856 } else { 1857 return interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL); 1858 } 1859 } 1860 1861 1869 public void align(Collection componentIds, boolean closed, int dimension, int alignment) { 1870 if (logTestCode()) { 1871 testCode.add("// > ALIGN"); testCode.add("{"); LayoutTestUtils.writeCollection(testCode, "componentIds", componentIds); testCode.add("boolean closed = " + closed + ";"); testCode.add("int dimension = " + dimension + ";"); testCode.add("int alignment = " + alignment + ";"); testCode.add("ld.align(componentIds, closed, dimension, alignment);"); testCode.add("}"); } 1880 LayoutInterval[] intervals = new LayoutInterval[componentIds.size()]; 1881 int counter = 0; 1882 Iterator iter = componentIds.iterator(); 1883 while (iter.hasNext()) { 1884 String id = (String )iter.next(); 1885 LayoutComponent component = layoutModel.getLayoutComponent(id); 1886 intervals[counter++] = component.getLayoutInterval(dimension); 1887 } 1888 modelListener.deactivate(); 1889 try { 1890 new LayoutAligner(this, layoutModel, operations).alignIntervals(intervals, closed, dimension, alignment); 1891 } finally { 1892 modelListener.activate(); 1893 } 1894 requireStructureOptimization(); 1895 if (logTestCode()) { 1896 testCode.add("// < ALIGN"); } 1898 } 1899 1900 private void destroyRedundantGroups(Set updatedContainers) { 1901 Iterator it = layoutModel.getAllComponents(); 1902 while (it.hasNext()) { 1903 LayoutComponent comp = (LayoutComponent) it.next(); 1904 if (!comp.isLayoutContainer()) 1905 continue; 1906 1907 boolean updated = false; 1908 for (int dim=0; dim<DIM_COUNT; dim++) { 1909 LayoutInterval interval = comp.getLayoutRoot(dim); 1910 updated = updated || destroyRedundantGroups(interval); 1911 } 1912 if (updated) { 1913 updatedContainers.add(comp); 1914 } 1915 } 1916 } 1917 1918 private boolean destroyRedundantGroups(LayoutInterval interval) { 1919 boolean updated = false; 1920 for (int i=interval.getSubIntervalCount()-1; i>=0; i--) { 1921 if (i >= interval.getSubIntervalCount()) continue; 1922 LayoutInterval subInterval = interval.getSubInterval(i); 1923 if (subInterval.isGroup()) { 1924 destroyRedundantGroups(subInterval); 1925 destroyGroupIfRedundant(subInterval, interval); 1926 updated |= (subInterval.getParent() == null); 1927 } 1928 } 1929 return updated; 1930 } 1931 1932 1940 void destroyGroupIfRedundant(LayoutInterval group, LayoutInterval boundary) { 1941 if ((group == null) || (!group.isGroup()) || (group == boundary)) return; 1942 LayoutInterval parent = group.getParent(); 1943 if (parent == null) return; 1945 1946 if (group.getSubIntervalCount() == 0) { 1948 takeOutInterval(group, boundary); 1949 return; 1950 } 1951 1952 if (operations.dissolveRedundantGroup(group)) { 1953 destroyGroupIfRedundant(parent, boundary); 1954 } 1955 } 1956 1957 1966 void takeOutInterval(LayoutInterval interval, LayoutInterval boundary) { 1967 LayoutInterval parent = interval.getParent(); 1968 int index = parent.indexOf(interval); 1969 List toRemove = new LinkedList(); 1970 toRemove.add(interval); 1971 if (parent.isSequential()) { 1972 if (index > 0) { 1974 LayoutInterval li = parent.getSubInterval(index-1); 1975 if (li.isEmptySpace()) { 1976 toRemove.add(li); 1977 } 1978 } 1979 if (index+1 < parent.getSubIntervalCount()) { 1981 LayoutInterval li = parent.getSubInterval(index+1); 1982 if (li.isEmptySpace()) { 1983 toRemove.add(li); 1984 } 1985 } 1986 if ((toRemove.size() == 3) && (parent.getSubIntervalCount() > 3)) { 1988 LayoutInterval gap = new LayoutInterval(SINGLE); 1989 if (interval.isComponent() && (interval.getComponent().getLayoutInterval(VERTICAL) == interval)) { 1990 int alignment = LayoutInterval.getEffectiveAlignment(interval); 1991 int size = 0; 1992 for (int i=0; i<3; i++) { 1993 size += LayoutInterval.getIntervalCurrentSize((LayoutInterval)toRemove.get(i), VERTICAL); 1994 } 1995 gap.setSizes(NOT_EXPLICITLY_DEFINED, size, (alignment == TRAILING) ? Short.MAX_VALUE : USE_PREFERRED_SIZE); 1996 } 1997 layoutModel.addInterval(gap, parent, index); 1998 } 1999 } 2000 Iterator iter = toRemove.iterator(); 2001 while (iter.hasNext()) { 2002 LayoutInterval remove = (LayoutInterval)iter.next(); 2003 layoutModel.removeInterval(remove); 2004 } 2005 destroyGroupIfRedundant(parent, boundary); 2007 } 2008 2009 2011 2024 void createRemainderGroup(List list, LayoutInterval seq, 2025 int index, int position, int mainAlignment, int dimension) 2026 { 2027 assert seq.isSequential() && (position == LEADING || position == TRAILING); 2028 if (position == TRAILING) { 2029 index++; 2030 } 2031 2033 LayoutInterval gap = null; 2034 LayoutInterval leadingGap = null; 2035 LayoutInterval trailingGap = null; 2036 boolean onlyGaps = true; 2037 boolean gapLeads = true; 2038 boolean gapTrails = true; 2039 2040 for (int i = list.size()-1; i>=0; i--) { 2042 List subList = (List)list.get(i); 2043 if (subList.size() == 2) { int alignment = ((Integer )subList.get(0)).intValue(); 2045 LayoutInterval li = (LayoutInterval) subList.get(1); 2046 if (li.isEmptySpace()) { 2047 if (gap == null || li.getMaximumSize() > gap.getMaximumSize()) { 2048 gap = li; 2049 } 2050 if (isFixedPadding(li)) { 2051 if (alignment == LEADING) { 2052 leadingGap = li; 2053 gapTrails = false; 2054 } 2055 else if (alignment == TRAILING) { 2056 trailingGap = li; 2057 gapLeads = false; 2058 } 2059 } 2060 else { 2061 gapLeads = false; 2062 gapTrails = false; 2063 } 2064 list.remove(i); 2065 } 2066 else { 2067 onlyGaps = false; 2068 } 2069 } 2070 } 2071 2072 if (list.size() == 1) { List subList = (List) list.get(0); 2074 Iterator itr = subList.iterator(); 2075 itr.next(); do { 2077 LayoutInterval li = (LayoutInterval) itr.next(); 2078 layoutModel.addInterval(li, seq, index++); 2079 } 2080 while (itr.hasNext()); 2081 return; 2082 } 2083 2084 for (Iterator it=list.iterator(); it.hasNext(); ) { 2086 List subList = (List) it.next(); 2087 if (subList.size() != 2) { onlyGaps = false; 2089 2090 boolean first = true; 2091 Iterator itr = subList.iterator(); 2092 itr.next(); do { 2094 LayoutInterval li = (LayoutInterval) itr.next(); 2095 if (first) { 2096 first = false; 2097 if (isFixedPadding(li)) 2098 leadingGap = li; 2099 else 2100 gapLeads = false; 2101 } 2102 else if (!itr.hasNext()) { 2103 if (isFixedPadding(li)) 2104 trailingGap = li; 2105 else 2106 gapTrails = false; 2107 } 2108 } 2109 while (itr.hasNext()); 2110 } 2111 } 2112 2113 if (onlyGaps) { 2114 operations.insertGapIntoSequence(gap, seq, index, dimension); 2115 return; 2116 } 2117 2118 LayoutInterval group = new LayoutInterval(PARALLEL); 2120 if (position == mainAlignment) { 2121 group.setMinimumSize(USE_PREFERRED_SIZE); 2123 group.setMaximumSize(USE_PREFERRED_SIZE); 2124 } 2125 2127 for (Iterator it=list.iterator(); it.hasNext(); ) { 2129 List subList = (List) it.next(); 2130 2131 if (gapLeads) { 2132 subList.remove(1); 2133 } 2134 if (gapTrails) { 2135 subList.remove(subList.size()-1); 2136 } 2137 2138 LayoutInterval interval; 2139 if (subList.size() == 2) { int alignment = ((Integer )subList.get(0)).intValue(); 2141 interval = (LayoutInterval) subList.get(1); 2142 if (alignment == LEADING || alignment == TRAILING) { 2143 layoutModel.setIntervalAlignment(interval, alignment); 2144 } 2145 } 2146 else { interval = new LayoutInterval(SEQUENTIAL); 2148 Iterator itr = subList.iterator(); 2149 int alignment = ((Integer )itr.next()).intValue(); 2150 if (alignment == LEADING || alignment == TRAILING) { 2151 interval.setAlignment(alignment); 2152 } 2153 do { 2154 LayoutInterval li = (LayoutInterval) itr.next(); 2155 layoutModel.addInterval(li, interval, -1); 2156 } 2157 while (itr.hasNext()); 2158 } 2159 layoutModel.addInterval(interval, group, -1); 2160 } 2161 2162 if (gapLeads) { 2164 layoutModel.addInterval(leadingGap, seq, index++); 2165 } 2166 layoutModel.addInterval(group, seq, index++); 2167 if (gapTrails) { 2168 layoutModel.addInterval(trailingGap, seq, index); 2169 } 2170 } 2171 2172 static boolean isFixedPadding(LayoutInterval interval) { 2173 return interval.isEmptySpace() 2174 && (interval.getMinimumSize() == NOT_EXPLICITLY_DEFINED || interval.getMinimumSize() == USE_PREFERRED_SIZE) 2175 && interval.getPreferredSize() == NOT_EXPLICITLY_DEFINED 2176 && (interval.getMaximumSize() == NOT_EXPLICITLY_DEFINED || interval.getMaximumSize() == USE_PREFERRED_SIZE); 2177 } 2178 2179 2181 private int optimizeGaps(LayoutInterval group, int dimension, boolean recursive) { 2184 assert group.isParallel(); 2185 2186 if (recursive) { 2188 for (int i=0; i < group.getSubIntervalCount(); i++) { 2189 LayoutInterval li = group.getSubInterval(i); 2190 if (li.isParallel()) { 2191 optimizeGaps(li, dimension, recursive); 2192 } 2193 else if (li.isSequential()) { 2194 for (int ii=0; ii < li.getSubIntervalCount(); ii++) { 2195 LayoutInterval llii = li.getSubInterval(ii); 2196 if (llii.isParallel()) { 2197 int idx = optimizeGaps(llii, dimension, recursive); 2198 if (idx >= 0) ii = idx; 2200 } 2201 } 2202 } 2203 } 2204 } 2205 2206 if (group.getGroupAlignment() == CENTER || group.getGroupAlignment() == BASELINE) { 2207 return -1; 2208 } 2209 int nonEmptyCount = LayoutInterval.getCount(group, LayoutRegion.ALL_POINTS, true); 2210 if (nonEmptyCount <= 1) { 2211 if (group.getParent() == null) { 2212 if (group.getSubIntervalCount() > 1) { 2213 for (int i=group.getSubIntervalCount()-1; i >= 0; i--) { 2215 if (group.getSubInterval(i).isEmptySpace()) { 2216 layoutModel.removeInterval(group, i); 2217 break; 2218 } 2219 } 2220 } 2221 else if (group.getSubIntervalCount() == 0) { 2222 propEmptyContainer(group, dimension); 2224 } 2225 } else { assert (nonEmptyCount == 1); 2227 assert (group.getSubIntervalCount() == 1); 2228 LayoutInterval interval = group.getSubInterval(0); 2229 layoutModel.removeInterval(interval); 2231 layoutModel.setIntervalAlignment(interval, group.getAlignment()); 2232 LayoutInterval parent = group.getParent(); 2233 int index = layoutModel.removeInterval(group); 2234 if (parent.isSequential() && interval.isSequential()) { 2235 for (int i=interval.getSubIntervalCount()-1; i>=0; i--) { 2237 LayoutInterval subInterval = interval.getSubInterval(i); 2238 layoutModel.removeInterval(subInterval); 2239 layoutModel.addInterval(subInterval, parent, index); 2240 } 2241 eliminateConsecutiveGaps(parent, 0, dimension); 2242 } else { 2243 layoutModel.addInterval(interval, parent, index); 2244 } 2245 2260 } 2261 return -1; 2262 } 2263 2264 return operations.optimizeGaps(group, dimension); 2265 } 2266 2267 2269 2275 void setIntervalResizing(LayoutInterval interval, boolean resizable) { 2276 switchFillAttribute(interval, resizable); 2277 layoutModel.setIntervalSize(interval, 2278 resizable ? NOT_EXPLICITLY_DEFINED : USE_PREFERRED_SIZE, 2279 interval.getPreferredSize(), 2280 resizable ? Short.MAX_VALUE : USE_PREFERRED_SIZE); 2281 } 2282 2283 private void switchFillAttribute(LayoutInterval interval, boolean resizable) { 2285 if (resizable) { 2286 if (interval.hasAttribute(LayoutInterval.ATTRIBUTE_FILL)) { 2287 layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTRIBUTE_FORMER_FILL, true); 2288 layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTRIBUTE_FILL, false); 2289 } 2290 } else { 2291 if (interval.hasAttribute(LayoutInterval.ATTRIBUTE_FORMER_FILL)) { 2292 layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTRIBUTE_FORMER_FILL, false); 2293 layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTRIBUTE_FILL, true); 2294 } 2295 } 2296 } 2297 2298 2300 public void setDefaultSize(String compId) { 2301 if (logTestCode()) { 2302 testCode.add("// > SET DEFAULT SIZE"); testCode.add("{"); testCode.add("String compId = \"${" + compId + "}\";"); testCode.add("ld.setDefaultSize(compId);"); testCode.add("}"); } 2308 LayoutComponent component = layoutModel.getLayoutComponent(compId); 2309 if (component != null) 2310 setDefaultSize(component); 2311 if (logTestCode()) { 2312 testCode.add("// < SET DEFAULT SIZE"); } 2314 } 2315 2316 private void setDefaultSize(LayoutComponent component) { 2317 imposeSize = true; 2318 if (component.isLayoutContainer()) { 2319 for (Iterator it=component.getSubcomponents(); it.hasNext(); ) { 2320 LayoutComponent comp = (LayoutComponent) it.next(); 2321 if (comp.isLayoutContainer()) 2322 setDefaultSize(comp); 2323 } 2324 setDefaultSizeInContainer(component.getLayoutRoot(HORIZONTAL)); 2325 setDefaultSizeInContainer(component.getLayoutRoot(VERTICAL)); 2326 updateDesignModifications(component); 2327 } 2328 else { 2329 operations.resizeInterval(component.getLayoutInterval(HORIZONTAL), NOT_EXPLICITLY_DEFINED); 2330 operations.resizeInterval(component.getLayoutInterval(VERTICAL), NOT_EXPLICITLY_DEFINED); 2331 } 2332 } 2333 2334 private void setDefaultSizeInContainer(LayoutInterval interval) { 2335 if (!interval.isGroup()) { 2336 if (LayoutInterval.canResize(interval)) 2337 operations.resizeInterval(interval, 2338 interval.getMinimumSize() != USE_PREFERRED_SIZE ? interval.getMinimumSize() : NOT_EXPLICITLY_DEFINED); 2339 } 2340 else { 2341 for (Iterator it=interval.getSubIntervals(); it.hasNext(); ) { 2342 setDefaultSizeInContainer((LayoutInterval)it.next()); 2343 } 2344 } 2345 } 2346 2347 private void updateDesignModifications(LayoutComponent container) { 2348 if (imposeSize || optimizeStructure) { 2349 cleanDesignAttrs(container.getLayoutRoot(HORIZONTAL)); 2352 cleanDesignAttrs(container.getLayoutRoot(VERTICAL)); 2353 } 2354 else { 2355 updateDesignModifications(container.getLayoutRoot(HORIZONTAL), HORIZONTAL); 2356 updateDesignModifications(container.getLayoutRoot(VERTICAL), VERTICAL); 2357 } 2358 } 2359 2360 private void updateDesignModifications(LayoutInterval root, int dimension) { 2361 cleanDesignAttrs(root); 2362 findContainerResizingGap(root, dimension); 2363 } 2364 2365 private static void cleanDesignAttrs(LayoutInterval group) { 2366 group.unsetAttribute(LayoutInterval.DESIGN_ATTRS); 2367 for (int i=0,n=group.getSubIntervalCount(); i < n; i++) { 2368 LayoutInterval li = group.getSubInterval(i); 2369 if (li.isGroup()) 2370 cleanDesignAttrs(li); 2371 else 2372 li.unsetAttribute(LayoutInterval.DESIGN_ATTRS); 2373 } 2374 } 2375 2376 private void findContainerResizingGap(LayoutInterval rootInterval, int dimension) { 2377 if (!LayoutInterval.wantResize(rootInterval) && (LayoutInterval.getIntervalCurrentSize(rootInterval, dimension) != prefSizeOfInterval(rootInterval))) { 2379 return; 2381 } 2382 int gapPosition = TRAILING; 2384 LayoutInterval resGap = findContainerResizingGap(rootInterval, dimension, gapPosition); 2385 if (resGap == null) { 2386 gapPosition = LEADING; 2387 resGap = findContainerResizingGap(rootInterval, dimension, gapPosition); 2388 if (resGap == null) { 2389 gapPosition = -1; 2390 resGap = findContainerResizingGap(rootInterval, dimension, gapPosition); 2391 if (resGap == null) { 2392 return; 2393 } 2394 } 2395 } 2396 else if (!LayoutInterval.canResize(resGap)) { LayoutInterval gap = findContainerResizingGap(rootInterval, dimension, LEADING); 2398 if (gap != null && LayoutInterval.canResize(gap)) { 2399 resGap = gap; 2400 gapPosition = LEADING; 2401 } 2402 else { 2403 gap = findContainerResizingGap(rootInterval, dimension, -1); 2404 if (gap != null && LayoutInterval.canResize(gap)) { 2405 resGap = gap; 2406 gapPosition = -1; 2407 } 2408 } 2409 } 2410 2411 resGap.setAttribute(LayoutInterval.ATTR_DESIGN_CONTAINER_GAP 2413 | LayoutInterval.ATTR_DESIGN_RESIZING); 2414 2415 LayoutInterval sub = resGap; 2416 LayoutInterval parent = resGap.getParent(); 2417 do { 2418 if (parent.isSequential()) { 2419 for (Iterator it=parent.getSubIntervals(); it.hasNext(); ) { 2420 LayoutInterval li = (LayoutInterval) it.next(); 2421 if (li != sub) { 2422 li.setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING); 2423 } 2424 } 2425 } 2426 else { for (Iterator it=parent.getSubIntervals(); it.hasNext(); ) { 2428 LayoutInterval interval = (LayoutInterval) it.next(); 2429 if (interval != sub) { 2430 assert interval.isSequential(); 2431 if (interval.isSequential()) { 2432 for (int i=0, n=interval.getSubIntervalCount(); i < n; i++) { 2433 LayoutInterval li = interval.getSubInterval(i); 2434 if (((i == 0 && gapPosition == LEADING) 2435 || (i+1 == n && gapPosition == TRAILING)) 2436 && canBeContainerResizingGap(li)) 2437 { li.setAttribute(LayoutInterval.ATTR_DESIGN_RESIZING); 2439 } 2440 else li.setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING); 2441 } 2442 } 2443 else { 2444 interval.setAttribute(LayoutInterval.ATTR_DESIGN_SUPPRESSED_RESIZING); 2445 } 2446 } 2447 } 2448 } 2449 sub = parent; 2450 parent = sub.getParent(); 2451 } 2452 while (parent != null); 2453 } 2454 2455 private static LayoutInterval findContainerResizingGap(LayoutInterval group, int dimension, int alignment) { 2456 assert group.isParallel(); 2457 2458 LayoutInterval theGap = null; 2459 int gapSize = Integer.MAX_VALUE; 2460 2461 for (Iterator it=group.getSubIntervals(); it.hasNext(); ) { 2462 LayoutInterval seq = (LayoutInterval) it.next(); 2463 if (!seq.isSequential()) { 2464 return null; 2465 } 2466 2467 int n = seq.getSubIntervalCount(); 2468 if (alignment == LEADING || alignment == TRAILING) { 2469 LayoutInterval li = seq.getSubInterval(alignment == LEADING ? 0 : n-1); 2471 LayoutInterval gap; 2472 if (canBeContainerResizingGap(li) 2473 && (LayoutInterval.wantResize(seq) 2474 || LayoutInterval.getEffectiveAlignment(li) == (alignment^1))) 2475 { gap = li; 2477 } 2478 else if (li.isParallel()) { 2479 gap = findContainerResizingGap(li, dimension, alignment); 2480 } 2481 else gap = null; 2483 if (gap == null) { 2484 return null; 2485 } 2486 2487 LayoutInterval neighbor = LayoutInterval.getDirectNeighbor(gap, alignment^1, false); 2488 int p1 = neighbor.getCurrentSpace().positions[dimension][alignment]; 2489 int p2 = group.getCurrentSpace().positions[dimension][alignment]; 2490 int size = Math.abs(p2-p1); 2491 2492 if (theGap == null || size < gapSize) { 2493 theGap = gap; 2494 gapSize = size; 2495 } 2496 } 2500 else { for (int i=n-2; i > 0; i--) { 2502 LayoutInterval li = seq.getSubInterval(i); 2503 if (canBeContainerResizingGap(li) && LayoutInterval.canResize(li)) { 2504 return group.getSubIntervalCount() == 1 ? li : null; 2505 } 2506 } 2507 return null; 2508 } 2509 } 2510 2511 return theGap; 2512 } 2513 2514 private static boolean canBeContainerResizingGap(LayoutInterval li) { 2515 return li.isEmptySpace() 2516 && (li.getPreferredSize() != NOT_EXPLICITLY_DEFINED || li.getMaximumSize() >= Short.MAX_VALUE); 2517 } 2518 2519 2523 private boolean imposeCurrentContainerSize(LayoutComponent component, LayoutDragger.SizeDef[] resizingDef, boolean recursive) { 2524 assert component.isLayoutContainer(); 2525 2526 Rectangle interior = visualMapper.getContainerInterior(component.getId()); 2527 if (interior == null) 2528 return false; component.setCurrentInterior(interior); 2530 2531 if (component.getParent() != null) { 2532 Rectangle bounds = visualMapper.getComponentBounds(component.getId()); 2533 component.setCurrentBounds(bounds, 2534 visualMapper.getBaselinePosition(component.getId(), bounds.width, bounds.height)); 2535 } 2536 for (Iterator it=component.getSubcomponents(); it.hasNext(); ) { 2537 LayoutComponent subComp = (LayoutComponent) it.next(); 2538 Rectangle bounds = visualMapper.getComponentBounds(subComp.getId()); 2539 subComp.setCurrentBounds(bounds, 2540 visualMapper.getBaselinePosition(subComp.getId(), bounds.width, bounds.height)); 2541 if (subComp.isLayoutContainer()) { 2542 if (recursive) 2543 imposeCurrentContainerSize(subComp, null, true); 2544 } 2545 else imposeCurrentComponentSize(subComp); 2546 } 2547 Dimension minimum = null; 2548 Dimension preferred = null; 2549 for (int i=0; i < DIM_COUNT; i++) { 2550 LayoutInterval outer = component.getLayoutInterval(i); 2551 int currentSize = outer.getCurrentSpace().size(i); 2552 LayoutInterval root = component.getLayoutRoot(i); 2553 if (root.getSubIntervalCount() == 0) { propEmptyContainer(root, i); 2555 } 2556 else { if (resizingDef != null && resizingDef[i] != null) { 2558 LayoutInterval resGap = resizingDef[i].getResizingGap(); 2559 if (resGap != null) { int size = resizingDef[i].getResizingGapSize(currentSize); 2561 if (size == 0) { LayoutInterval gapParent = resGap.getParent(); 2563 assert gapParent.isSequential(); 2564 int index = layoutModel.removeInterval(resGap); 2565 assert index == 0 || index == gapParent.getSubIntervalCount(); 2566 if (gapParent.getSubIntervalCount() == 1) { 2567 LayoutInterval last = layoutModel.removeInterval(gapParent, 0); 2568 operations.addContent(last, gapParent.getParent(), layoutModel.removeInterval(gapParent)); 2569 } 2570 else if (LayoutInterval.canResize(resGap) && !LayoutInterval.wantResize(root)) { 2571 index = index == 0 ? gapParent.getSubIntervalCount()-1 : 0; 2573 LayoutInterval otherGap = gapParent.getSubInterval(index); 2574 if (otherGap.isEmptySpace()) { layoutModel.setIntervalSize(otherGap, 2576 NOT_EXPLICITLY_DEFINED, otherGap.getPreferredSize(), Short.MAX_VALUE); 2577 } 2578 } 2579 } 2580 else { operations.resizeInterval(resGap, size); 2582 if (size == NOT_EXPLICITLY_DEFINED && LayoutInterval.canResize(resGap)) { 2583 resGap.setMaximumSize(USE_PREFERRED_SIZE); 2585 boolean layoutResizing = LayoutInterval.wantResize(root); 2586 resGap.setMaximumSize(Short.MAX_VALUE); 2587 if (layoutResizing) { layoutModel.setIntervalSize(resGap, 2589 NOT_EXPLICITLY_DEFINED, NOT_EXPLICITLY_DEFINED, USE_PREFERRED_SIZE); 2590 } 2591 } 2592 } 2593 } 2594 else if (!LayoutInterval.wantResize(root)) { 2595 int minLayoutSize = computeMinimumDesignSize(root); 2597 int growth = root.getCurrentSpace().size(i) - minLayoutSize; 2598 if (growth > 0) { LayoutInterval endGap = new LayoutInterval(SINGLE); 2600 endGap.setSizes(NOT_EXPLICITLY_DEFINED, growth, Short.MAX_VALUE); 2601 operations.insertGap(endGap, root, minLayoutSize, i, TRAILING); 2602 } 2603 } 2604 } 2605 updateLayoutStructure(root, i, true); 2606 } 2607 2608 if (component.getParent() != null) { 2609 if (minimum == null) { 2610 minimum = visualMapper.getComponentMinimumSize(component.getId()); 2611 preferred = visualMapper.getComponentPreferredSize(component.getId()); 2612 } 2613 int min = i == HORIZONTAL ? minimum.width : minimum.height; 2614 boolean externalSize = 2615 (visualMapper.hasExplicitPreferredSize(component.getId()) 2616 && currentSize != (i==HORIZONTAL ? preferred.width:preferred.height)) 2617 || currentSize < min 2618 || (currentSize > min && !LayoutInterval.wantResize(root)); 2619 operations.resizeInterval(outer, externalSize ? currentSize : NOT_EXPLICITLY_DEFINED); 2620 } 2621 } 2622 2623 return true; 2624 } 2625 2626 private void imposeCurrentComponentSize(LayoutComponent component) { 2627 Dimension preferred = visualMapper.getComponentPreferredSize(component.getId()); 2628 for (int i=0; i < DIM_COUNT; i++) { 2629 LayoutInterval li = component.getLayoutInterval(i); 2630 int defPref = li.getPreferredSize(); 2631 if (LayoutInterval.wantResizeInLayout(li) && defPref != 0) { int current = li.getCurrentSpace().size(i); 2634 int pref = i == HORIZONTAL ? preferred.width : preferred.height; 2635 if (defPref == NOT_EXPLICITLY_DEFINED) 2636 defPref = pref; 2637 if (defPref != current) 2638 operations.resizeInterval(li, current != pref ? current : NOT_EXPLICITLY_DEFINED); 2639 } 2640 } 2641 } 2642 2643 private void imposeCurrentGapSize(LayoutInterval gap, int currentSize, int dimension) { 2644 int pad = -1; 2645 int min = gap.getMinimumSize(); 2646 int pref = gap.getPreferredSize(); 2647 if (pref == NOT_EXPLICITLY_DEFINED) { 2648 if (!LayoutInterval.wantResizeInLayout(gap)) 2649 return; pad = LayoutUtils.getSizeOfDefaultGap(gap, visualMapper); 2651 pref = pad; 2652 } 2653 if (currentSize != pref) { if (min == NOT_EXPLICITLY_DEFINED) { 2655 if (pad < 0) { 2656 pad = LayoutUtils.getSizeOfDefaultGap(gap, visualMapper); 2657 } 2658 min = pad; 2659 } 2660 else if (min == USE_PREFERRED_SIZE) { 2661 min = pref; 2662 } 2663 if (currentSize < min) { 2664 currentSize = min; 2665 } 2666 operations.resizeInterval(gap, currentSize == pad ? NOT_EXPLICITLY_DEFINED : currentSize); 2667 } 2668 } 2669 2670 private void propEmptyContainer(LayoutInterval root, int dimension) { 2671 assert root.getParent() == null && root.getSubIntervalCount() == 0; 2672 LayoutInterval gap = new LayoutInterval(SINGLE); 2673 gap.setSizes(0, root.getCurrentSpace().size(dimension), Short.MAX_VALUE); 2674 layoutModel.addInterval(gap, root, 0); 2675 } 2676 2677 private int computeMinimumDesignSize(LayoutInterval interval) { 2678 int size = 0; 2679 if (interval.isSingle()) { 2680 int min = interval.getMinimumSize(true); 2681 size = min == USE_PREFERRED_SIZE ? interval.getPreferredSize(true) : min; 2682 if (size == NOT_EXPLICITLY_DEFINED) { 2683 if (interval.isComponent()) { 2684 LayoutComponent comp = interval.getComponent(); 2685 Dimension dim = min == USE_PREFERRED_SIZE ? 2686 visualMapper.getComponentPreferredSize(comp.getId()) : 2687 visualMapper.getComponentMinimumSize(comp.getId()); 2688 size = interval==comp.getLayoutInterval(HORIZONTAL) ? dim.width : dim.height; 2689 } 2690 else { size = LayoutUtils.getSizeOfDefaultGap(interval, visualMapper); 2692 } 2693 } 2694 } 2695 else if (interval.isSequential()) { 2696 for (int i=0, n=interval.getSubIntervalCount(); i < n; i++) { 2697 size += computeMinimumDesignSize(interval.getSubInterval(i)); 2698 } 2699 } 2700 else { for (int i=0, n=interval.getSubIntervalCount(); i < n; i++) { 2702 size = Math.max(size, computeMinimumDesignSize(interval.getSubInterval(i))); 2703 } 2704 } 2705 return size; 2706 } 2707 2708 2710 private void intervalRemoved(LayoutInterval parent, int index, boolean primary, boolean wasResizing, int dimension) { 2712 if (parent.isSequential()) { 2713 LayoutInterval leadingGap; 2714 LayoutInterval leadingNeighbor; 2715 if (index > 0) { 2716 LayoutInterval li = parent.getSubInterval(index-1); 2717 if (li.isEmptySpace()) { 2718 leadingGap = li; 2719 layoutModel.removeInterval(li); 2720 index--; 2721 leadingNeighbor = index > 0 ? parent.getSubInterval(index-1) : null; 2722 } 2723 else { 2724 leadingGap = null; 2725 leadingNeighbor = li; 2726 } 2727 } 2728 else { 2729 leadingGap = null; 2730 leadingNeighbor = null; 2731 } 2732 2733 LayoutInterval trailingGap; 2734 LayoutInterval trailingNeighbor; 2735 if (index < parent.getSubIntervalCount()) { 2736 LayoutInterval li = parent.getSubInterval(index); 2737 if (li.isEmptySpace()) { 2738 trailingGap = li; 2739 layoutModel.removeInterval(li); 2740 trailingNeighbor = index < parent.getSubIntervalCount() ? 2741 parent.getSubInterval(index) : null; 2742 } 2743 else { 2744 trailingGap = null; 2745 trailingNeighbor = li; 2746 } 2747 } 2748 else { 2749 trailingGap = null; 2750 trailingNeighbor = null; 2751 } 2752 2753 if (!wasResizing 2754 && ((leadingGap != null && LayoutInterval.canResize(leadingGap)) 2755 || (trailingGap != null && LayoutInterval.canResize(trailingGap)))) 2756 wasResizing = true; 2757 2758 LayoutInterval superParent = parent.getParent(); 2759 2760 if (parent.getSubIntervalCount() == 0) { int idx = layoutModel.removeInterval(parent); 2763 if (superParent.getParent() != null) { 2764 intervalRemoved(superParent, idx, false, wasResizing, dimension); 2765 } 2766 else if (superParent.getSubIntervalCount() == 0) { propEmptyContainer(superParent, dimension); 2768 } 2769 } 2770 else { boolean restResizing = LayoutInterval.contentWantResize(parent); 2772 if (wasResizing && !restResizing) { 2773 if (leadingNeighbor == null && parent.getAlignment() == LEADING) { 2774 layoutModel.setIntervalAlignment(parent, TRAILING); 2775 } 2776 if (trailingNeighbor == null && parent.getAlignment() == TRAILING) { 2777 layoutModel.setIntervalAlignment(parent, LEADING); 2778 } 2779 } 2780 2781 int cutSize = LayoutRegion.distance( 2782 (leadingNeighbor != null ? leadingNeighbor : parent).getCurrentSpace(), 2783 (trailingNeighbor != null ? trailingNeighbor : parent).getCurrentSpace(), 2784 dimension, 2785 leadingNeighbor != null ? TRAILING : LEADING, 2786 trailingNeighbor != null ? LEADING : TRAILING); 2787 2788 if ((leadingNeighbor != null && trailingNeighbor != null) || superParent.getParent() == null || (leadingNeighbor != null && LayoutInterval.getEffectiveAlignment(leadingNeighbor, TRAILING) == TRAILING) 2791 || (trailingNeighbor != null && LayoutInterval.getEffectiveAlignment(trailingNeighbor, LEADING) == LEADING)) 2792 { int min, max; 2794 if (wasResizing && !restResizing) { min = NOT_EXPLICITLY_DEFINED; 2796 max = Short.MAX_VALUE; 2797 } 2798 else { 2799 min = max = USE_PREFERRED_SIZE; 2800 } 2801 LayoutInterval gap = new LayoutInterval(SINGLE); 2802 gap.setSizes(min, cutSize, max); 2803 layoutModel.addInterval(gap, parent, index); 2804 } 2805 else { if (parent.getSubIntervalCount() == 1) { 2807 LayoutInterval last = layoutModel.removeInterval(parent, 0); 2808 layoutModel.addInterval(last, superParent, layoutModel.removeInterval(parent)); 2809 layoutModel.setIntervalAlignment(last, parent.getRawAlignment()); 2810 } 2811 else { int l = (trailingNeighbor != null && leadingNeighbor == null ? 2814 trailingNeighbor : parent).getCurrentSpace().positions[dimension][LEADING]; 2815 int t = (leadingNeighbor != null && trailingNeighbor == null ? 2816 leadingNeighbor : parent).getCurrentSpace().positions[dimension][TRAILING]; 2817 parent.getCurrentSpace().set(dimension, l, t); 2818 } 2819 maintainSize(superParent, wasResizing || restResizing, dimension, 2820 parent, parent.getCurrentSpace().size(dimension) - cutSize); 2821 } 2822 2823 if (wasResizing && !restResizing) { 2824 operations.enableGroupResizing(superParent); } 2826 } 2827 } 2828 else { 2829 if (parent.getParent() == null) return; assert parent.isParallel() && parent.getSubIntervalCount() > 0; 2831 2832 int groupAlign = parent.getGroupAlignment(); 2833 if (primary && (groupAlign == LEADING || groupAlign == TRAILING)) { 2834 maintainSize(parent, wasResizing, dimension, null, 0); 2835 } 2836 2837 if (parent.getSubIntervalCount() == 1 && parent.getParent() != null) { LayoutInterval remaining = parent.getSubInterval(0); 2840 layoutModel.removeInterval(remaining); 2841 layoutModel.setIntervalAlignment(remaining, parent.getAlignment()); 2842 if (LayoutInterval.wantResize(remaining) && !LayoutInterval.canResize(parent)) { 2843 if (remaining.isGroup()) 2845 operations.suppressGroupResizing(remaining); 2846 else 2847 layoutModel.setIntervalSize(remaining, 2848 USE_PREFERRED_SIZE, remaining.getPreferredSize(), USE_PREFERRED_SIZE); 2849 } 2850 LayoutInterval superParent = parent.getParent(); 2851 int i = layoutModel.removeInterval(parent); 2852 operations.addContent(remaining, superParent, i); 2853 if (remaining.isSequential() && superParent.isSequential()) { 2854 eliminateConsecutiveGaps(superParent, i, dimension); 2857 } 2858 } 2860 else if (wasResizing && !LayoutInterval.contentWantResize(parent)) { 2861 operations.enableGroupResizing(parent); 2862 } 2863 } 2864 } 2865 2866 private void eliminateConsecutiveGaps(LayoutInterval group, int index, int dimension) { 2867 assert group.isSequential(); 2868 if (index > 0) 2869 index--; 2870 while (index < group.getSubIntervalCount()-1) { 2871 LayoutInterval current = group.getSubInterval(index); 2872 LayoutInterval next = group.getSubInterval(index+1); 2873 if (current.isEmptySpace() && next.isEmptySpace()) { 2874 int la; 2875 LayoutRegion lr; 2876 if (index > 0) { 2877 la = TRAILING; 2878 lr = group.getSubInterval(index-1).getCurrentSpace(); 2879 } 2880 else { 2881 la = LEADING; 2882 lr = group.getCurrentSpace(); 2883 } 2884 int ta; 2885 LayoutRegion tr; 2886 if (index+2 < group.getSubIntervalCount()) { 2887 ta = LEADING; 2888 tr = group.getSubInterval(index+2).getCurrentSpace(); 2889 } 2890 else { 2891 ta = TRAILING; 2892 tr = group.getCurrentSpace(); 2893 } 2894 operations.eatGap(current, next, LayoutRegion.distance(lr, tr, dimension, la, ta)); 2895 } 2896 else index++; 2897 } 2898 } 2899 2900 2901 private void maintainSize(LayoutInterval group, boolean wasResizing, int dimension, 2902 LayoutInterval excluded, int excludedSize) 2903 { 2904 assert group.isParallel(); 2906 int groupSize = group.getCurrentSpace().size(dimension); 2907 int[] groupPos = group.getCurrentSpace().positions[dimension]; 2908 2909 boolean leadAlign = false; 2910 boolean trailAlign = false; 2911 int leadCompPos = Integer.MAX_VALUE; 2912 int trailCompPos = Integer.MIN_VALUE; 2913 int subSize = Integer.MIN_VALUE; 2914 2915 Iterator it = group.getSubIntervals(); 2916 while (it.hasNext()) { 2917 LayoutInterval li = (LayoutInterval) it.next(); 2918 int align = li.getAlignment(); 2919 int l, t; if (li != excluded) { 2921 int size = li.getCurrentSpace().size(dimension); 2922 if (size >= groupSize) { 2923 return; 2924 } 2925 if (size > subSize) { 2926 subSize = size; 2927 } 2928 l = LayoutUtils.getOutermostComponent(li, dimension, LEADING) 2929 .getCurrentSpace().positions[dimension][LEADING]; 2930 t = LayoutUtils.getOutermostComponent(li, dimension, TRAILING) 2931 .getCurrentSpace().positions[dimension][TRAILING]; 2932 } 2933 else { 2934 if (excludedSize > subSize) { 2935 subSize = excludedSize; 2936 } 2937 if (align == LEADING) { 2938 l = groupPos[LEADING]; 2939 t = groupPos[LEADING] + excludedSize; 2940 } 2941 else { l = groupPos[TRAILING] - excludedSize; 2943 t = groupPos[TRAILING]; 2944 } 2945 } 2946 if (l < leadCompPos) 2947 leadCompPos = l; 2948 if (t > trailCompPos) 2949 trailCompPos = t; 2950 2951 if (align == LEADING) 2952 leadAlign = true; 2953 else trailAlign = true; 2955 } 2956 2957 if (leadAlign && trailAlign) { 2958 optimizeGaps(group, dimension, false); 2959 } 2960 else { if (!LayoutInterval.canResize(group)) { wasResizing = false; 2963 } 2964 boolean resizing = LayoutInterval.wantResize(group); 2965 LayoutInterval parent = group.getParent(); 2966 if (parent != null && parent.isParallel() 2967 && group.getAlignment() == (leadAlign ? LEADING : TRAILING)) 2968 { maintainSize(parent, wasResizing && !resizing, dimension, group, subSize); 2970 if (leadAlign) { 2971 groupPos[TRAILING] = trailCompPos; 2972 } 2973 if (trailAlign) { 2974 groupPos[LEADING] = leadCompPos; 2975 } 2976 groupPos[CENTER] = (groupPos[LEADING] + groupPos[LEADING]) / 2; 2977 } 2978 else { 2979 int increment = groupSize - subSize; 2980 assert increment > 0; 2981 LayoutInterval gap = new LayoutInterval(SINGLE); 2982 int min, max; 2983 if (!resizing && (wasResizing || parent == null)) { 2984 min = NOT_EXPLICITLY_DEFINED; 2985 max = Short.MAX_VALUE; 2986 } 2987 else { 2988 min = max = USE_PREFERRED_SIZE; 2989 } 2990 gap.setSizes(min, increment, max); 2991 2992 operations.insertGap(gap, group, leadAlign ? trailCompPos : leadCompPos, 2993 dimension, leadAlign ? TRAILING : LEADING); 2994 2995 if (leadAlign) { 2996 groupPos[TRAILING] = trailCompPos; 2997 } 2998 if (trailAlign) { 2999 groupPos[LEADING] = leadCompPos; 3000 } 3001 groupPos[CENTER] = (groupPos[LEADING] + groupPos[LEADING]) / 2; 3002 3003 if (parent != null) { 3004 if (parent.isSequential()) { 3005 parent = parent.getParent(); 3006 } 3007 optimizeGaps(parent, dimension, false); 3008 } 3009 } 3010 } 3011 } 3012 3013 public String [] positionCode() { 3014 return dragger.positionCode(); 3015 } 3016 3017 3020 private int[] cursorPos = { 0, 0 }; 3022 3023 private boolean[][] resizability = { { true, true }, { true, true } }; 3025 3026 3029 static final String TEST_SWITCH = "netbeans.form.layout_test"; 3031 3032 public List testCode = new ArrayList(); 3033 3034 private List testCode0 = new ArrayList(); 3036 private List beforeMove = new ArrayList(); 3037 private List move1 = new ArrayList(); 3038 private List move2 = new ArrayList(); 3039 private boolean isMoving = false; 3040 3041 private int modelCounter = -1; 3042 3043 private Point lastMovePoint = new Point (0, 0); 3044 3045 public int getModelCounter() { 3046 return modelCounter; 3047 } 3048 3049 public void setModelCounter(int modelCounter) { 3050 this.modelCounter = modelCounter; 3051 } 3052 3053 public static boolean testMode() { 3054 return Boolean.getBoolean(TEST_SWITCH); 3055 } 3056 3057 public boolean logTestCode() { 3058 return modelCounter > -1 && Boolean.getBoolean(TEST_SWITCH); 3059 } 3060} 3061 | Popular Tags |