1 19 20 package org.netbeans.modules.form.layoutdesign; 21 22 import java.util.*; 23 24 29 class LayoutAligner implements LayoutConstants { 30 31 private LayoutDesigner designer; 32 33 private LayoutModel layoutModel; 34 35 private LayoutOperations operations; 36 37 44 LayoutAligner(LayoutDesigner designer, LayoutModel layoutModel, LayoutOperations operations) { 45 this.designer = designer; 46 this.layoutModel = layoutModel; 47 this.operations = operations; 48 } 49 50 58 void alignIntervals(LayoutInterval[] intervals, boolean closed, int dimension, int alignment) { 59 LayoutInterval parParent = LayoutInterval.getCommonParent(intervals); 61 if (parParent.isSequential()) { 62 parParent = parParent.getParent(); 63 } 64 65 markByAlignAttributes(parParent, intervals); 67 List removedSeqs = new LinkedList(); 68 parParent = splitByAlignAttrs(parParent, removedSeqs, alignment, closed, dimension, false); 69 70 List gapsToResize = transferToParallelParent(intervals, parParent, alignment, closed); 72 LayoutInterval returnPar = LayoutInterval.getFirstParent(parParent, PARALLEL); 73 returnRemovedIntervals((returnPar == null) ? parParent : returnPar, removedSeqs, dimension); 74 75 if (alignment != CENTER) { 76 Map leadingMap = new HashMap(); 78 Map trailingMap = new HashMap(); 79 for (int i=0; i<intervals.length; i++) { 80 LayoutInterval interval = intervals[i]; 81 LayoutInterval parent = interval.getParent(); 82 if (parent == parParent) parent = interval; 84 LayoutInterval leading = (LayoutInterval)leadingMap.get(parent); 85 LayoutInterval trailing = (LayoutInterval)trailingMap.get(parent); 86 if ((leading == null) || (parent.indexOf(leading) > parent.indexOf(interval))) { 87 leadingMap.put(parent, interval); 88 } 89 if ((trailing == null) || (parent.indexOf(trailing) < parent.indexOf(interval))) { 90 trailingMap.put(parent, interval); 91 } 92 } 93 LayoutInterval[] leadingIntervals = new LayoutInterval[leadingMap.size()]; 95 LayoutInterval[] trailingIntervals = new LayoutInterval[trailingMap.size()]; 96 Iterator iter = leadingMap.values().iterator(); 97 int counter = 0; 98 while (iter.hasNext()) { 99 LayoutInterval interval = (LayoutInterval)iter.next(); 100 leadingIntervals[counter++] = interval; 101 } 102 iter = trailingMap.values().iterator(); 103 counter = 0; 104 while (iter.hasNext()) { 105 LayoutInterval interval = (LayoutInterval)iter.next(); 106 trailingIntervals[counter++] = interval; 107 } 108 109 if (closed) { 111 align(leadingIntervals, trailingIntervals, true, dimension, alignment); 112 } else { 113 LayoutInterval[] itervalsToAlign = (alignment == LEADING) ? leadingIntervals : trailingIntervals; 114 align(itervalsToAlign, null, false, dimension, alignment); 115 } 116 117 iter = gapsToResize.iterator(); 119 while (iter.hasNext()) { 120 LayoutInterval gap = (LayoutInterval)iter.next(); 121 designer.setIntervalResizing(gap, true); 122 } 123 } 124 125 designer.destroyGroupIfRedundant(parParent, null); 127 } 128 129 141 private void markByAlignAttributes(LayoutInterval parParent, LayoutInterval[] intervals) { 142 layoutModel.changeIntervalAttribute(parParent, LayoutInterval.ATTR_ALIGN_PRE, true); 143 layoutModel.changeIntervalAttribute(parParent, LayoutInterval.ATTR_ALIGN_POST, true); 144 for (int i=0; i<intervals.length; i++) { 145 LayoutInterval interval = intervals[i]; 146 while (interval != parParent) { 147 LayoutInterval parent = interval.getParent(); 148 layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTR_ALIGN_PRE, true); 149 layoutModel.changeIntervalAttribute(interval, LayoutInterval.ATTR_ALIGN_POST, true); 150 if (parent.isSequential()) { 151 int index = parent.indexOf(interval); 152 for (int j=0; j<parent.getSubIntervalCount(); j++) { 153 if (j < index) { 154 markByAlignAttribute(parent.getSubInterval(j), LayoutInterval.ATTR_ALIGN_PRE); 155 } else if (j > index) { 156 markByAlignAttribute(parent.getSubInterval(j), LayoutInterval.ATTR_ALIGN_POST); 157 } 158 } 159 } 160 interval = parent; 161 } 162 } 163 } 164 165 172 private void markByAlignAttribute(LayoutInterval interval, int attr) { 173 layoutModel.changeIntervalAttribute(interval, attr, true); 174 if (interval.isGroup()) { 175 Iterator iter = interval.getSubIntervals(); 176 while (iter.hasNext()) { 177 markByAlignAttribute((LayoutInterval)iter.next(), attr); 178 } 179 } 180 } 181 182 private LayoutInterval splitByAlignAttrs(LayoutInterval interval, List removedSeqs, int alignment, boolean closed, int dimension, boolean optimize) { 183 if (interval.isGroup()) { 184 for (int i=interval.getSubIntervalCount()-1; i>=0; i--) { 185 LayoutInterval subInterval = interval.getSubInterval(i); 186 splitByAlignAttrs(subInterval, removedSeqs, alignment, closed, dimension, true); 187 } 188 if (interval.isParallel()) { 189 if (interval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE) 190 && interval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST)) { 191 LayoutInterval prePar = new LayoutInterval(PARALLEL); 192 layoutModel.changeIntervalAttribute(prePar, LayoutInterval.ATTR_ALIGN_PRE, true); 193 LayoutInterval midPar = new LayoutInterval(PARALLEL); 194 layoutModel.changeIntervalAttribute(midPar, LayoutInterval.ATTR_ALIGN_PRE, true); 195 layoutModel.changeIntervalAttribute(midPar, LayoutInterval.ATTR_ALIGN_POST, true); 196 LayoutInterval postPar = new LayoutInterval(PARALLEL); 197 layoutModel.changeIntervalAttribute(postPar, LayoutInterval.ATTR_ALIGN_POST, true); 198 199 int minMid = Short.MAX_VALUE; 201 int maxMid = Short.MIN_VALUE; 202 int maxPre = Short.MIN_VALUE; 203 int minPost = Short.MAX_VALUE; 204 int maxMidWidth = 0; 205 Iterator iter = interval.getSubIntervals(); 206 while (iter.hasNext()) { 207 LayoutInterval subInterval = (LayoutInterval)iter.next(); 208 boolean preSub = subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE); 209 boolean postSub = subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST); 210 LayoutInterval trailingPre = null; 211 LayoutInterval leadingMid = null; 212 LayoutInterval trailingMid = null; 213 LayoutInterval leadingPost = null; 214 if (subInterval.isSequential() && preSub && postSub) { 215 Iterator subIter = subInterval.getSubIntervals(); 216 while (subIter.hasNext()) { 217 LayoutInterval subSubInterval = (LayoutInterval)subIter.next(); 218 boolean preSubSub = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE); 219 boolean postSubSub = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST); 220 if (preSubSub) { 221 if (postSubSub) { 222 if (leadingMid == null) { 223 leadingMid = subSubInterval; 224 } 225 trailingMid = subSubInterval; 226 } else { 227 if (!subSubInterval.isEmptySpace() || (subSubInterval.getPreferredSize() == NOT_EXPLICITLY_DEFINED)) { 228 trailingPre = subSubInterval; 229 } 230 } 231 } else if (postSubSub) { 232 if ((leadingPost == null) && (!subSubInterval.isEmptySpace() || (subSubInterval.getPreferredSize() == NOT_EXPLICITLY_DEFINED))) { 233 leadingPost = subSubInterval; 234 } 235 } 236 } 237 } else { 238 if (preSub) { 239 if (postSub) { 240 leadingMid = subInterval; 241 trailingMid = subInterval; 242 } else { 243 trailingPre = subInterval; 244 } 245 } else if (postSub) { 246 leadingPost = subInterval; 247 } 248 } 249 250 if (trailingPre != null) { 251 maxPre = Math.max(maxPre, getPosition(trailingPre, dimension, TRAILING)); 252 } 253 if (leadingMid != null) { int midLeading = getPosition(leadingMid, dimension, LEADING); 255 int midTrailing = getPosition(trailingMid, dimension, TRAILING); 256 int width = midTrailing - midLeading; 257 minMid = Math.min(minMid, midLeading); 258 maxMid = Math.max(maxMid, midTrailing); 259 maxMidWidth = Math.max(maxMidWidth, width); 260 } 261 if (leadingPost != null) { 262 minPost = Math.min(minPost, getPosition(leadingPost, dimension, LEADING)); 263 } 264 } 265 266 for (int i=interval.getSubIntervalCount()-1; i>=0; i--) { 268 LayoutInterval subInterval = interval.getSubInterval(i); 269 int index = layoutModel.removeInterval(subInterval); 270 if (subInterval.isSequential()) { 271 if (!subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE) 272 && !subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST)) { 273 removedSeqs.add(subInterval); 274 continue; 275 } 276 LayoutInterval preSeq = new LayoutInterval(SEQUENTIAL); 277 layoutModel.changeIntervalAttribute(preSeq, LayoutInterval.ATTR_ALIGN_PRE, true); 278 LayoutInterval midSeq = new LayoutInterval(SEQUENTIAL); 279 layoutModel.changeIntervalAttribute(midSeq, LayoutInterval.ATTR_ALIGN_PRE, true); 280 layoutModel.changeIntervalAttribute(midSeq, LayoutInterval.ATTR_ALIGN_POST, true); 281 LayoutInterval postSeq = new LayoutInterval(SEQUENTIAL); 282 layoutModel.changeIntervalAttribute(postSeq, LayoutInterval.ATTR_ALIGN_POST, true); 283 284 int[] leading = new int[subInterval.getSubIntervalCount()]; 285 int[] trailing = new int[leading.length]; 286 for (int j=0; j<subInterval.getSubIntervalCount(); j++) { 287 LayoutInterval subSubInterval = subInterval.getSubInterval(j); 288 leading[j] = getPosition(subSubInterval, dimension, LEADING); 289 trailing[j] = getPosition(subSubInterval, dimension, TRAILING); 290 } 291 292 LayoutInterval lastPre = null; 294 LayoutInterval lastPreGap = null; 295 LayoutInterval firstPostGap = null; 296 LayoutInterval firstMid = null; 297 LayoutInterval lastMid = null; 298 LayoutInterval firstPost = null; 299 for (int j=0; j<subInterval.getSubIntervalCount(); j++) { 300 LayoutInterval subSubInterval = subInterval.getSubInterval(j); 301 boolean pre = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE); 302 boolean post = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST); 303 if (pre && !post) { 304 lastPreGap = subSubInterval; 305 } 306 if (post && !pre) { 307 if (firstPostGap == null) { 308 firstPostGap = subSubInterval; 309 } 310 } 311 if (pre && post) { 312 if (firstMid == null) { 313 firstMid = subSubInterval; 314 } 315 lastMid = subSubInterval; 316 } 317 } 318 firstPost = firstPostGap; 319 lastPre = lastPreGap; 320 if ((firstPostGap != null) && !firstPostGap.isEmptySpace()) { 321 firstPostGap = null; 322 } 323 if ((lastPreGap != null) && !lastPreGap.isEmptySpace()) { 324 lastPreGap = null; 325 } 326 if (alignment == LEADING) { 327 int bias = Math.max(0, maxPre - minMid); 328 int shift = (LayoutInterval.getEffectiveAlignment(firstMid) == LEADING) ? bias : 0; 329 if (lastPreGap != null) { 330 int delta = shortenGap(lastPreGap, getPosition(firstMid, dimension, LEADING) - minMid - shift); 331 trailing[subInterval.indexOf(lastPreGap)] -= delta; 332 } 333 shift = (LayoutInterval.getEffectiveAlignment(lastMid) == LEADING) ? bias : 0; 334 if (firstPostGap != null) { 335 int delta = shortenGap(firstPostGap, maxMidWidth - (getPosition(lastMid, dimension, TRAILING) - minMid) + shift); 336 leading[subInterval.indexOf(firstPostGap)] += delta; 337 } 338 } 339 if (alignment == TRAILING) { 340 int bias = Math.max(0, maxMid - minPost); 341 int shift = (LayoutInterval.getEffectiveAlignment(lastMid) == TRAILING) ? bias : 0; 342 if (firstPostGap != null) { 343 int delta = shortenGap(firstPostGap, maxMid - getPosition(lastMid, dimension, TRAILING) - shift); 344 leading[subInterval.indexOf(firstPostGap)] += delta; 345 } 346 shift = (LayoutInterval.getEffectiveAlignment(firstMid) == TRAILING) ? bias : 0; 347 if (lastPreGap != null) { 348 int delta = shortenGap(lastPreGap, maxMidWidth - (maxMid - getPosition(firstMid, dimension, LEADING)) + shift); 349 trailing[subInterval.indexOf(lastPreGap)] -= delta; 350 } 351 } 352 353 setAlignmentAccordingEffectiveAlignment(preSeq, lastPre); 355 setAlignmentAccordingEffectiveAlignment(midSeq, firstMid); 356 setAlignmentAccordingEffectiveAlignment(postSeq, firstPost); 357 358 for (int j=subInterval.getSubIntervalCount()-1; j>=0; j--) { 359 LayoutInterval subSubInterval = subInterval.getSubInterval(j); 360 boolean pre = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE); 361 boolean post = subSubInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST); 362 assert pre || post; 363 LayoutInterval seqToInsertInto; 364 if (pre && !post && ((alignment == LEADING) || closed)) { 365 seqToInsertInto = preSeq; 366 } else if (post && !pre && ((alignment == TRAILING) || closed)) { 367 seqToInsertInto = postSeq; 368 } else { 369 seqToInsertInto = midSeq; 370 } 371 expandCurrentSpace(seqToInsertInto, dimension, leading[j], trailing[j]); 372 layoutModel.removeInterval(subSubInterval); 373 layoutModel.addInterval(subSubInterval, seqToInsertInto, 0); 374 } 375 putGroupToGroup(preSeq, prePar, 0); 376 putGroupToGroup(midSeq, midPar, 0); 377 putGroupToGroup(postSeq, postPar, 0); 378 } else { 379 boolean pre = subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_PRE); 380 boolean post = subInterval.hasAttribute(LayoutInterval.ATTR_ALIGN_POST); 381 if (!pre && !post) { 382 removedSeqs.add(subInterval); 383 } else if (pre && !post && ((alignment == LEADING) || closed)) { 384 layoutModel.addInterval(subInterval, prePar, 0); 385 prePar.getCurrentSpace().expand(subInterval.getCurrentSpace()); 386 } else if (post && !pre && ((alignment == TRAILING) || closed)) { 387 layoutModel.addInterval(subInterval, postPar, 0); 388 postPar.getCurrentSpace().expand(subInterval.getCurrentSpace()); 389 } else { 390 layoutModel.addInterval(subInterval, midPar, 0); 391 midPar.getCurrentSpace().expand(subInterval.getCurrentSpace()); 392 } 393 } 394 } 395 LayoutInterval parent = interval.getParent(); 396 int index; 397 if (parent != null) { 398 index = layoutModel.removeInterval(interval); 399 } else { 400 parent = interval; 401 index = 0; 402 } 403 if (!parent.isSequential()) { 404 LayoutInterval seq = new LayoutInterval(SEQUENTIAL); 405 layoutModel.changeIntervalAttribute(seq, LayoutInterval.ATTR_ALIGN_PRE, true); 406 layoutModel.changeIntervalAttribute(seq, LayoutInterval.ATTR_ALIGN_POST, true); 407 layoutModel.addInterval(seq, parent, index); 408 index = 0; 409 parent = seq; 410 } 411 putGroupToGroup(postPar, parent, index); 412 interval = putGroupToGroup(midPar, parent, index, optimize); 413 putGroupToGroup(prePar, parent, index); 414 } 415 } 416 } 417 return interval; 418 } 419 420 private LayoutInterval putGroupToGroup(LayoutInterval groupToInsert, LayoutInterval group, int index) { 421 return putGroupToGroup(groupToInsert, group, index, true); 422 } 423 424 private LayoutInterval putGroupToGroup(LayoutInterval groupToInsert, LayoutInterval group, int index, boolean optimize) { 425 if (groupToInsert.isParallel()) { 427 LayoutInterval emptySpace = null; 428 for (int i=groupToInsert.getSubIntervalCount()-1; i>=0; i--) { 429 LayoutInterval interval = groupToInsert.getSubInterval(i); 430 if (interval.isEmptySpace()) { 431 emptySpace = interval; 432 layoutModel.removeInterval(interval); 433 } 434 } 435 if ((groupToInsert.getSubIntervalCount() == 0) && (emptySpace != null)) { 436 layoutModel.addInterval(emptySpace, groupToInsert, 0); 438 } 439 } 440 if (groupToInsert.getSubIntervalCount() > 0) { 441 LayoutRegion region = groupToInsert.getCurrentSpace(); 442 while (optimize && (groupToInsert.getSubIntervalCount() == 1)) { 443 LayoutInterval interval = groupToInsert.getSubInterval(0); 444 layoutModel.removeInterval(interval); 445 layoutModel.setIntervalAlignment(interval, groupToInsert.getAlignment()); 446 groupToInsert = interval; 447 } 448 if (optimize && groupToInsert.isSequential() && group.isSequential()) { 449 for (int i=groupToInsert.getSubIntervalCount()-1; i>=0; i--) { 450 LayoutInterval subInterval = groupToInsert.getSubInterval(i); 451 layoutModel.removeInterval(subInterval); 452 layoutModel.addInterval(subInterval, group, index); 453 } 454 groupToInsert = null; 455 } else { 456 layoutModel.addInterval(groupToInsert, group, index); 457 } 458 group.getCurrentSpace().expand(region); 459 } 460 return groupToInsert; 461 } 462 463 471 private void expandCurrentSpace(LayoutInterval interval, int dimension, int leading, int trailing) { 472 LayoutRegion region = new LayoutRegion(); 473 region.positions[dimension][LEADING] = leading; 474 region.positions[dimension][TRAILING] = trailing; 475 interval.getCurrentSpace().expand(region, dimension); 476 } 477 478 private int getPosition(LayoutInterval interval, int dimension, int alignment) { 479 if (interval.isEmptySpace()) { 480 LayoutInterval parent = interval.getParent(); 481 assert parent.isSequential() && ((alignment == LEADING) || (alignment == TRAILING)); 482 int index = parent.indexOf(interval); 483 if (alignment == LEADING) { 484 return (index > 0) ? 485 parent.getSubInterval(index-1).getCurrentSpace().positions[dimension][TRAILING] : 486 parent.getCurrentSpace().positions[dimension][LEADING]; 487 } else { return (index+1 < parent.getSubIntervalCount()) ? 489 parent.getSubInterval(index+1).getCurrentSpace().positions[dimension][LEADING] : 490 parent.getCurrentSpace().positions[dimension][TRAILING]; 491 } 492 } else { 493 return interval.getCurrentSpace().positions[dimension][alignment]; 494 } 495 } 496 497 506 private List transferToParallelParent(LayoutInterval[] intervals, LayoutInterval parParent, int alignment, boolean closed) { 507 LayoutComponent temp = intervals[0].getComponent(); 509 int dimension = (temp.getLayoutInterval(HORIZONTAL) == intervals[0]) ? HORIZONTAL : VERTICAL; 510 511 int leadingPosition = Short.MAX_VALUE; 513 int trailingPosition = 0; 514 int targetEffAlignment = LayoutConstants.DEFAULT; 515 for (int i=0; i<intervals.length; i++) { 516 LayoutInterval interval = intervals[i]; 517 518 assert interval.isComponent(); 520 521 LayoutRegion region = interval.getCurrentSpace(); 522 int leading = region.positions[dimension][LEADING]; 523 int trailing = region.positions[dimension][TRAILING]; 524 leadingPosition = Math.min(leading, leadingPosition); 525 trailingPosition = Math.max(trailing, trailingPosition); 526 527 int effAlignment = LayoutInterval.getEffectiveAlignment(interval); 528 if (((effAlignment == LEADING) || (effAlignment == TRAILING)) 529 && ((targetEffAlignment == DEFAULT) || (effAlignment == alignment))) { 530 targetEffAlignment = effAlignment; 531 } 532 } 533 534 boolean resizable = false; 535 boolean sequenceResizable; 536 boolean leadingGaps = true; 537 boolean trailingGaps = true; 538 List gapsToResize = new LinkedList(); 539 List sequenceGapsToResize; 540 LayoutInterval[] firstIntervals = new LayoutInterval[intervals.length]; 541 LayoutInterval[] lastIntervals = new LayoutInterval[intervals.length]; 542 543 List intervalList = Arrays.asList(intervals); 545 List newSequences = new LinkedList(); 546 Map gapSizes = new HashMap(); 547 for (int i=0; i<intervals.length; i++) { 548 LayoutInterval interval = intervals[i]; 549 550 List transferedComponents = transferedComponents(intervals, i, parParent); 552 LayoutInterval firstInterval = null; 553 LayoutInterval lastInterval = null; 554 for (int j = transferedComponents.size()-1; j>=0; j--) { 555 LayoutInterval trInterval = (LayoutInterval)transferedComponents.get(j); 556 if (intervalList.contains(trInterval)) { 557 firstInterval = trInterval; 558 if (lastInterval == null) { 559 lastInterval = trInterval; 560 } 561 } else if (alignment == CENTER) { 562 transferedComponents.remove(trInterval); 563 } 564 } 565 firstIntervals[i] = firstInterval; 566 lastIntervals[i] = lastInterval; 567 568 List newSequenceList = new LinkedList(); 570 newSequences.add(newSequenceList); 571 sequenceResizable = false; 572 sequenceGapsToResize = new LinkedList(); 573 Iterator iter = transferedComponents.iterator(); 574 575 LayoutRegion parentRegion = parParent.getCurrentSpace(); 577 LayoutInterval leadingInterval = (LayoutInterval)iter.next(); 578 LayoutRegion leadingRegion = leadingInterval.getCurrentSpace(); 579 if ((alignment == TRAILING) && !closed) { 580 int preGap = leadingRegion.positions[dimension][LEADING] 581 - parentRegion.positions[dimension][LEADING]; 582 LayoutInterval gapInterval = LayoutInterval.getNeighbor(leadingInterval, SEQUENTIAL, LEADING); 583 leadingGaps = leadingGaps && (preGap != 0); 584 if ((gapInterval != null) && gapInterval.isEmptySpace() && parParent.isParentOf(gapInterval) 585 && (LayoutInterval.getIntervalCurrentSize(gapInterval, dimension) == preGap)) { 586 LayoutInterval gap = cloneGap(gapInterval); 587 newSequenceList.add(gap); 588 gapSizes.put(gap, new Integer (preGap)); 589 if (alignment == TRAILING) { 590 sequenceResizable = sequenceResizable || LayoutInterval.canResize(gap); 591 } 592 } else { 593 maybeAddGap(newSequenceList, preGap, true); 594 if ((preGap != 0) && (alignment == TRAILING) && (leadingInterval == firstInterval)) { 595 LayoutInterval gap = (LayoutInterval)newSequenceList.get(newSequenceList.size() - 1); 596 if (LayoutInterval.getEffectiveAlignment(leadingInterval) == TRAILING) { 597 layoutModel.setIntervalSize(gap, USE_PREFERRED_SIZE, preGap, USE_PREFERRED_SIZE); 598 sequenceGapsToResize.add(gap); 599 } 600 } 601 } 602 } 603 604 boolean afterDefiningInterval = false; 606 newSequenceList.add(leadingInterval); 607 while (iter.hasNext()) { 608 if (leadingInterval == interval) { 609 afterDefiningInterval = true; 610 } 611 LayoutInterval trailingInterval = (LayoutInterval)iter.next(); 612 if (((alignment == TRAILING) && (!afterDefiningInterval || (leadingInterval == interval))) 613 || ((alignment == LEADING) && afterDefiningInterval)) { 614 sequenceResizable = sequenceResizable || LayoutInterval.canResize(leadingInterval); 615 } 616 617 LayoutRegion trailingRegion = trailingInterval.getCurrentSpace(); 619 LayoutInterval gapInterval = LayoutInterval.getNeighbor(leadingInterval, SEQUENTIAL, TRAILING); 620 int gapSize = trailingRegion.positions[dimension][LEADING] 621 - leadingRegion.positions[dimension][TRAILING]; 622 boolean gapFound = false; 623 if (gapInterval.isEmptySpace()) { 624 LayoutInterval neighbor = LayoutInterval.getNeighbor(gapInterval, SEQUENTIAL, TRAILING); 625 if (neighbor == trailingInterval) { 626 gapFound = true; 627 LayoutInterval gap = cloneGap(gapInterval); 628 newSequenceList.add(gap); 629 gapSizes.put(gap, new Integer (gapSize)); 630 if (((alignment == TRAILING) && !afterDefiningInterval) 631 || ((alignment == LEADING) && afterDefiningInterval)) { 632 sequenceResizable = sequenceResizable || LayoutInterval.canResize(gap); 633 } 634 } 635 } 636 if (!gapFound) { 637 maybeAddGap(newSequenceList, gapSize, (alignment == CENTER)); 638 } 639 if (((leadingInterval == lastInterval) && (alignment == LEADING) 640 && (LayoutInterval.getEffectiveAlignment(trailingInterval) == TRAILING)) 641 || ((trailingInterval == firstInterval) && (alignment == TRAILING)) 642 && (LayoutInterval.getEffectiveAlignment(leadingInterval) == LEADING)) { 643 LayoutInterval gap = (LayoutInterval)newSequenceList.get(newSequenceList.size() - 1); 644 if (!LayoutInterval.canResize(gap)) { 645 sequenceGapsToResize.add(gap); 646 } 647 } 648 649 newSequenceList.add(trailingInterval); 650 leadingInterval = trailingInterval; 651 leadingRegion = trailingRegion; 652 } 653 654 if ((alignment == LEADING) || ((alignment == TRAILING) && (leadingInterval == lastInterval))) { 656 sequenceResizable = sequenceResizable || LayoutInterval.canResize(leadingInterval); 657 } 658 if ((alignment == LEADING) && !closed) { 659 int postGap = parentRegion.positions[dimension][TRAILING] 660 - leadingRegion.positions[dimension][TRAILING]; 661 trailingGaps = trailingGaps && (postGap != 0); 662 LayoutInterval gapInterval = LayoutInterval.getNeighbor(leadingInterval, SEQUENTIAL, TRAILING); 663 if ((gapInterval != null) && gapInterval.isEmptySpace() && parParent.isParentOf(gapInterval) 664 && (LayoutInterval.getIntervalCurrentSize(gapInterval, dimension) == postGap)) { 665 LayoutInterval gap = cloneGap(gapInterval); 666 newSequenceList.add(gap); 667 gapSizes.put(gap, new Integer (postGap)); 668 if (alignment == LEADING) { 669 sequenceResizable = sequenceResizable || LayoutInterval.canResize(gap); 670 } 671 } else { 672 maybeAddGap(newSequenceList, postGap, true); 673 } 674 } 675 resizable = resizable || sequenceResizable; 676 if (!sequenceResizable) { 677 gapsToResize.addAll(sequenceGapsToResize); 678 } 679 } 680 681 if (alignment != CENTER) { 683 Iterator listIter = newSequences.iterator(); 684 for (int i=0; i<intervals.length; i++) { 685 List newSequenceList = (List)listIter.next(); 686 Iterator iter = newSequenceList.iterator(); 687 LayoutInterval gapCandidate = null; 688 while (iter.hasNext()) { 689 LayoutInterval interval = (LayoutInterval)iter.next(); 690 if (((interval == firstIntervals[i]) && (alignment == LEADING)) 691 || ((interval == lastIntervals[i]) && (alignment == TRAILING))) { 692 LayoutRegion region = interval.getCurrentSpace(); 693 int diff = 0; 694 if (alignment == TRAILING) { 695 if (iter.hasNext()) { 696 gapCandidate = (LayoutInterval)iter.next(); 697 diff = trailingPosition - region.positions[dimension][TRAILING]; 698 } else { 699 break; 700 } 701 } else { 702 diff = region.positions[dimension][LEADING] - leadingPosition; 703 } 704 if ((gapCandidate != null) && (gapCandidate.isEmptySpace())) { 705 if ((!leadingGaps && (alignment == LEADING) && (newSequenceList.indexOf(gapCandidate) == 0)) 706 || (!trailingGaps && (alignment == TRAILING) && !iter.hasNext())) { 707 newSequenceList.remove(gapCandidate); 708 } else { 709 Integer size = (Integer )gapSizes.get(gapCandidate); 710 int minSize = gapCandidate.getMinimumSize(); 711 int prefSize = gapCandidate.getPreferredSize(); 712 int maxSize = gapCandidate.getMaximumSize(); 713 if (diff > 0) { 714 if (size != null) { 715 int actualSize = size.intValue(); 716 diff += prefSize - actualSize; 717 } 718 if (minSize >= 0) { 719 minSize = (minSize - diff > 0) ? minSize - diff : NOT_EXPLICITLY_DEFINED; 720 } 721 if (prefSize >= 0) { 722 prefSize = (prefSize - diff > 0) ? prefSize - diff : NOT_EXPLICITLY_DEFINED; 723 } 724 if ((maxSize >= 0) && (maxSize != Short.MAX_VALUE)) { 725 maxSize = (maxSize - diff > 0) ? maxSize - diff : USE_PREFERRED_SIZE; 726 } 727 } 728 if ((targetEffAlignment == alignment) && (maxSize == Short.MAX_VALUE)) { 729 maxSize = USE_PREFERRED_SIZE; 730 } 731 layoutModel.setIntervalSize(gapCandidate, minSize, prefSize, maxSize); 732 } 733 } 734 break; 735 } 736 gapCandidate = interval; 737 } 738 } 739 } 740 741 Iterator listIter = newSequences.iterator(); 744 while (listIter.hasNext()) { 745 List newSequenceList = (List)listIter.next(); 746 LayoutInterval newSequence = new LayoutInterval(SEQUENTIAL); 747 if (alignment == CENTER) { 748 newSequence.setAlignment(CENTER); 749 } 750 Iterator iter = newSequenceList.iterator(); 751 int sequenceAlignment = DEFAULT; 752 while (iter.hasNext()) { 753 LayoutInterval compInterval = (LayoutInterval)iter.next(); 754 if (compInterval.isComponent()) { if (sequenceAlignment == DEFAULT) { 756 sequenceAlignment = LayoutInterval.getEffectiveAlignment(compInterval); 757 } 758 designer.takeOutInterval(compInterval, parParent); 759 layoutModel.setIntervalAlignment(compInterval, DEFAULT); 760 } 761 layoutModel.addInterval(compInterval, newSequence, -1); 762 } 763 if ((alignment != CENTER) && !LayoutInterval.wantResize(newSequence)) { 764 newSequence.setAlignment(sequenceAlignment); 765 } 766 if (newSequenceList.size() == 1) { 767 LayoutInterval compInterval = (LayoutInterval)newSequenceList.get(0); 768 layoutModel.removeInterval(compInterval); 769 if (newSequence.getAlignment() != DEFAULT) { 770 layoutModel.setIntervalAlignment(compInterval, newSequence.getAlignment()); 771 } 772 newSequence = compInterval; 773 } 774 layoutModel.addInterval(newSequence, parParent, -1); 775 } 776 777 if ((gapsToResize.size() > 0) && !resizable && (alignment != CENTER)) { 779 operations.suppressGroupResizing(parParent); 780 Iterator iter = gapsToResize.iterator(); 781 while (iter.hasNext()) { 782 LayoutInterval gap = (LayoutInterval)iter.next(); 783 layoutModel.changeIntervalAttribute(gap, LayoutInterval.ATTRIBUTE_FORMER_FILL, false); 784 layoutModel.changeIntervalAttribute(gap, LayoutInterval.ATTRIBUTE_FILL, true); 785 } 786 } 787 return gapsToResize; 788 } 789 790 798 private List transferedComponents(LayoutInterval[] intervals, int index, LayoutInterval parParent) { 799 LayoutInterval interval = intervals[index]; 800 LayoutInterval oppInterval = oppositeComponentInterval(interval); 801 List transferedComponents = new LinkedList(); 802 List components = new LinkedList(); 803 componentsInGroup(parParent, components); 804 808 Iterator iter = components.iterator(); 809 while (iter.hasNext()) { 810 LayoutInterval candidate = (LayoutInterval)iter.next(); 811 LayoutInterval oppCandidate = oppositeComponentInterval(candidate); 812 if (alignedIntervals(oppInterval, oppCandidate, BASELINE) 813 || alignedIntervals(oppInterval, oppCandidate, LEADING) 814 || alignedIntervals(oppInterval, oppCandidate, TRAILING) 815 || alignedIntervals(oppInterval, oppCandidate, CENTER)) { 816 if (parParent.isParentOf(candidate)) { 817 transferedComponents.add(candidate); 818 } 819 } 820 } 821 if (!transferedComponents.contains(interval)) { 822 transferedComponents.add(interval); 823 } 824 825 Collections.sort(transferedComponents, new Comparator() { 827 public int compare(Object o1, Object o2) { 828 LayoutInterval interval1 = (LayoutInterval)o1; 829 LayoutInterval interval2 = (LayoutInterval)o2; 830 LayoutComponent comp = interval1.getComponent(); 831 int dimension = (comp.getLayoutInterval(VERTICAL) == interval1) 832 ? VERTICAL : HORIZONTAL; 833 LayoutRegion region1 = interval1.getCurrentSpace(); 834 LayoutRegion region2 = interval2.getCurrentSpace(); 835 int value1 = region1.positions[dimension][LEADING]; 836 int value2 = region2.positions[dimension][LEADING]; 837 return (value1 - value2); 838 } 839 }); 840 return transferedComponents; 841 } 842 843 851 private void componentsInGroup(LayoutInterval group, Collection components) { 852 Iterator iter = group.getSubIntervals(); 853 while (iter.hasNext()) { 854 LayoutInterval interval = (LayoutInterval)iter.next(); 855 if (interval.isGroup()) { 856 componentsInGroup(interval, components); 857 } else if (interval.isComponent() && !components.contains(interval)) { 858 components.add(interval); 859 } 860 } 861 } 862 863 867 private boolean align(LayoutInterval[] leadingInts, LayoutInterval[] trailingInts, boolean closed, int dimension, int alignment) { 868 LayoutInterval commonGroup = null; 870 LayoutInterval[] intervals = leadingInts; 871 for (int i=0; i < intervals.length; i++) { 872 LayoutInterval interval = intervals[i]; 873 LayoutInterval parent = interval.getParent(); 874 if (!parent.isParallel()) { 875 parent = parent.getParent(); 876 assert parent.isParallel(); 877 } 878 if (commonGroup == null || (parent != commonGroup && parent.isParentOf(commonGroup))) { 879 commonGroup = parent; 880 } 881 else { 882 assert parent == commonGroup || commonGroup.isParentOf(parent); 883 } 884 } 885 886 List aligned = new LinkedList(); 888 List restLeading = new LinkedList(); 889 List restTrailing = new LinkedList(); 890 int mainEffectiveAlign = -1; 891 int originalCount = commonGroup.getSubIntervalCount(); 892 893 for (int i=0; i < intervals.length; i++) { 894 LayoutInterval interval = intervals[i]; 895 LayoutInterval parent = interval.getParent(); 896 LayoutInterval parParent = parent.isParallel() ? parent : parent.getParent(); 897 if (parParent != commonGroup) { 898 interval = getAlignSubstitute(interval, commonGroup, alignment); 899 if (interval == null) { 900 return false; } 902 parent = interval.getParent(); 903 } 904 905 if (parent.isSequential()) { 906 mainEffectiveAlign = LayoutInterval.getEffectiveAlignment(interval); 908 int extractCount = operations.extract(interval, closed ? trailingInts[i] : interval, alignment, closed, 910 restLeading, restTrailing); 911 if (extractCount == 1) { layoutModel.removeInterval(parent); 913 aligned.add(interval); 914 } 915 else { aligned.add(parent); 917 } 918 } 919 else { 920 aligned.add(interval); 921 } 922 } 923 924 LayoutInterval group; 926 LayoutInterval commonSeq; 927 boolean remainder = !restLeading.isEmpty() || !restTrailing.isEmpty(); 928 929 if ((!remainder && mainEffectiveAlign == alignment) 930 || (aligned.size() == originalCount 931 && commonGroup.getParent() != null)) 932 { group = commonGroup; 934 if (remainder) { LayoutInterval groupParent = group.getParent(); 936 if (groupParent.isSequential()) { 937 commonSeq = groupParent; 938 } 939 else { int index = layoutModel.removeInterval(group); 941 commonSeq = new LayoutInterval(SEQUENTIAL); 942 commonSeq.setAlignment(group.getAlignment()); 943 layoutModel.addInterval(commonSeq, groupParent, index); 944 layoutModel.setIntervalAlignment(group, DEFAULT); 945 layoutModel.addInterval(group, commonSeq, -1); 946 } 947 } 948 else commonSeq = null; 949 } 950 else { group = new LayoutInterval(PARALLEL); 952 if (remainder) { commonSeq = new LayoutInterval(SEQUENTIAL); 954 commonSeq.add(group, -1); 955 layoutModel.addInterval(commonSeq, commonGroup, -1); 956 } 957 else { 958 commonSeq = null; 959 layoutModel.addInterval(group, commonGroup, -1); 960 } 961 layoutModel.setGroupAlignment(group, alignment); 962 } 963 964 for (Iterator it=aligned.iterator(); it.hasNext(); ) { 967 LayoutInterval interval = (LayoutInterval) it.next(); 968 if (interval.getParent() != group) { 969 layoutModel.removeInterval(interval); 970 operations.addContent(interval, group, -1); 971 } 972 layoutModel.setIntervalAlignment(interval, alignment); 973 } 974 975 if (!restLeading.isEmpty()) { 977 designer.createRemainderGroup(restLeading, commonSeq, commonSeq.indexOf(group), LEADING, mainEffectiveAlign, dimension); 979 } 980 if (!restTrailing.isEmpty()) { 981 designer.createRemainderGroup(restTrailing, commonSeq, commonSeq.indexOf(group), TRAILING, mainEffectiveAlign, dimension); 983 } 984 985 return true; 986 } 987 988 993 private LayoutInterval cloneGap(LayoutInterval interval) { 994 assert interval.isEmptySpace(); 995 LayoutInterval gap = new LayoutInterval(SINGLE); 996 gap.setMinimumSize(interval.getMinimumSize()); 997 gap.setPreferredSize(interval.getPreferredSize()); 998 gap.setMaximumSize(interval.getMaximumSize()); 999 return gap; 1000 } 1001 1002 1009 private void maybeAddGap(List list, int size, boolean forceSize) { 1010 if (size > 0) { 1011 LayoutInterval gapInterval = new LayoutInterval(SINGLE); 1012 if (forceSize) { 1013 layoutModel.setIntervalSize(gapInterval, size, size, size); 1014 } 1015 list.add(gapInterval); 1016 } 1017 } 1018 1019 private static boolean compatibleGroupAlignment(int groupAlign, int align) { 1020 return groupAlign == align 1021 || ((groupAlign == LEADING || groupAlign == TRAILING) 1022 && (align == LEADING || align == TRAILING || align == DEFAULT)); 1023 } 1024 1025 private static boolean alignedIntervals(LayoutInterval interval1, LayoutInterval interval2, int alignment) { 1026 LayoutInterval commonParent; 1027 LayoutInterval otherInterval; 1028 if (interval1.isParentOf(interval2)) { 1029 commonParent = interval1; 1030 otherInterval = interval2; 1031 } 1032 else if (interval2.isParentOf(interval1)) { 1033 commonParent = interval2; 1034 otherInterval = interval1; 1035 } 1036 else { 1037 commonParent = interval1.getParent(); 1038 while (commonParent != null) { 1039 if (!hasAlignmentInParent(interval1, alignment)) { 1040 return false; 1041 } 1042 if (commonParent.isParentOf(interval2)) { 1043 break; 1044 } 1045 interval1 = commonParent; 1046 commonParent = interval1.getParent(); 1047 } 1048 if (commonParent == null) { 1049 return false; 1050 } 1051 otherInterval = interval2; 1052 } 1053 1054 do { 1055 if (!hasAlignmentInParent(otherInterval, alignment)) { 1056 return false; 1057 } 1058 otherInterval = otherInterval.getParent(); 1059 } 1060 while (otherInterval != commonParent); 1061 return true; 1062 } 1063 1064 private static boolean hasAlignmentInParent(LayoutInterval interval, int alignment) { 1065 LayoutInterval parent = interval.getParent(); 1066 if (parent.isSequential()) { 1067 if (alignment == LEADING) { 1068 return parent.getSubInterval(0) == interval; 1069 } 1070 if (alignment == TRAILING) { 1071 return parent.getSubInterval(parent.getSubIntervalCount()-1) == interval; 1072 } 1073 return false; 1074 } 1075 else { assert interval.getAlignment() != alignment || compatibleGroupAlignment(parent.getGroupAlignment(), alignment); 1077 return interval.getAlignment() == alignment 1078 || LayoutInterval.wantResize(interval); 1079 } 1080 } 1081 1082 private static LayoutInterval getAlignSubstitute(LayoutInterval toAlignWith, LayoutInterval commonParParent, int alignment) { 1083 assert alignment == LEADING || alignment == TRAILING; 1084 1085 while (toAlignWith != null && LayoutInterval.getFirstParent(toAlignWith, PARALLEL) != commonParParent) { 1086 if (LayoutInterval.isAlignedAtBorder(toAlignWith, alignment)) { 1087 toAlignWith = toAlignWith.getParent(); 1088 } 1089 else return null; 1090 } 1091 return toAlignWith; 1092 } 1093 1094 1102 private LayoutInterval oppositeComponentInterval(LayoutInterval interval) { 1103 assert interval.isComponent(); 1104 LayoutComponent component = interval.getComponent(); 1105 int oppDimension = (component.getLayoutInterval(HORIZONTAL) == interval) 1106 ? VERTICAL : HORIZONTAL; 1107 return component.getLayoutInterval(oppDimension); 1108 } 1109 1110 private int shortenGap(LayoutInterval gap, int delta) { 1111 assert gap.isEmptySpace(); 1112 int prefSize = gap.getPreferredSize(); 1113 if (prefSize == NOT_EXPLICITLY_DEFINED) { 1114 return 0; } else { 1116 int newPref = prefSize - delta; 1117 newPref = (newPref > 0) ? newPref : NOT_EXPLICITLY_DEFINED; if (LayoutInterval.canResize(gap)) { 1119 layoutModel.setIntervalSize(gap, NOT_EXPLICITLY_DEFINED, newPref, Short.MAX_VALUE); 1120 } else { 1121 layoutModel.setIntervalSize(gap, USE_PREFERRED_SIZE, newPref, USE_PREFERRED_SIZE); 1122 } 1123 return (prefSize > delta) ? delta : prefSize; 1124 } 1125 } 1126 1127 private void returnRemovedIntervals(LayoutInterval parParent, List removed, int dimension) { 1128 LayoutRegion parRegion = parParent.getCurrentSpace(); 1129 Iterator iter = removed.iterator(); 1130 while (iter.hasNext()) { 1131 LayoutInterval interval = (LayoutInterval)iter.next(); 1132 LayoutRegion region = interval.getCurrentSpace(); 1133 int pre = Math.max(0, region.positions[dimension][LEADING] - parRegion.positions[dimension][LEADING]); 1134 int post = Math.max(0, parRegion.positions[dimension][TRAILING] - region.positions[dimension][TRAILING]); 1135 if (((pre != 0) || (post != 0)) && !interval.isSequential()) { 1136 LayoutInterval seq = new LayoutInterval(SEQUENTIAL); 1137 if (interval.getAlignment() != BASELINE) 1138 layoutModel.setIntervalAlignment(seq, interval.getAlignment()); 1139 layoutModel.setIntervalAlignment(interval, DEFAULT); 1140 layoutModel.addInterval(interval, seq, -1); 1141 interval = seq; 1142 } 1143 1144 if (pre != 0) { 1146 LayoutInterval first = interval.getSubInterval(0); 1147 if (first.isEmptySpace()) { 1148 layoutModel.removeInterval(first); 1149 region = interval.getSubInterval(0).getCurrentSpace(); 1150 pre = Math.max(0, region.positions[dimension][LEADING] - parRegion.positions[dimension][LEADING]); 1151 } 1152 } 1153 if (post != 0) { 1154 LayoutInterval last = interval.getSubInterval(interval.getSubIntervalCount()-1); 1155 if (last.isEmptySpace()) { 1156 layoutModel.removeInterval(last); 1157 region = interval.getSubInterval(interval.getSubIntervalCount()-1).getCurrentSpace(); 1158 post = Math.max(0, parRegion.positions[dimension][TRAILING] - region.positions[dimension][TRAILING]); 1159 } 1160 } 1161 1162 if (pre != 0) { 1164 LayoutInterval gap = new LayoutInterval(SINGLE); 1165 gap.setSize(pre); 1166 if (interval.getAlignment() == TRAILING) designer.setIntervalResizing(gap, true); 1167 layoutModel.addInterval(gap, interval, 0); 1168 } 1169 if (post != 0) { 1170 LayoutInterval gap = new LayoutInterval(SINGLE); 1171 gap.setSize(post); 1172 if (interval.getAlignment() == LEADING) designer.setIntervalResizing(gap, true); 1173 layoutModel.addInterval(gap, interval, -1); 1174 } 1175 1176 layoutModel.addInterval(interval, parParent, -1); 1178 } 1179 } 1180 1181 private void setAlignmentAccordingEffectiveAlignment(LayoutInterval aligned, LayoutInterval interval) { 1182 if (interval == null) return; 1183 int alignment = LayoutInterval.getEffectiveAlignment(interval); 1184 if ((alignment == LEADING) || (alignment == TRAILING)) { 1185 layoutModel.setIntervalAlignment(aligned, alignment); 1186 } 1187 } 1188 1189} | Popular Tags |