1 19 20 package org.netbeans.modules.form.layoutdesign; 21 22 import java.awt.*; 23 import java.util.*; 24 import java.util.List ; 25 import javax.swing.undo.*; 26 27 28 38 39 public class LayoutModel implements LayoutConstants { 40 41 private Map idToComponents = new HashMap(); 43 44 private ArrayList listeners; 46 47 private boolean recordingChanges = true; 49 private boolean undoRedoInProgress; 50 private int changeMark; 51 private int oldestMark; 52 private int changeCountHardLimit = 10000; 53 private Map undoMap = new HashMap(500); 54 private Map redoMap = new HashMap(100); 55 private LayoutUndoableEdit lastUndoableEdit; 56 57 private boolean corrected; 59 60 62 67 public LayoutComponent getLayoutComponent(String compId) { 68 return (LayoutComponent) idToComponents.get(compId); 69 } 70 71 public void addRootComponent(LayoutComponent comp) { 72 addComponent(comp, null, -1); 73 } 74 75 public void removeComponent(String compId, boolean fromModel) { 76 LayoutComponent comp = getLayoutComponent(compId); 77 if (comp != null) 78 removeComponentAndIntervals(comp, fromModel); 79 } 80 81 84 public boolean changeComponentToContainer(String componentId) { 85 LayoutComponent component = getLayoutComponent(componentId); 86 if (component != null) { 87 setLayoutContainer(component, true); 88 return true; 89 } 90 return false; 91 } 92 93 99 public boolean changeContainerToComponent(String componentId) { 100 LayoutComponent component = getLayoutComponent(componentId); 101 if (component == null) { 102 return false; 103 } 104 for (int i=component.getSubComponentCount()-1; i>=0; i--) { 105 LayoutComponent sub = component.getSubComponent(i); 106 removeComponentAndIntervals(sub, !sub.isLayoutContainer()); 107 } 108 if (component.getParent() == null) { removeComponent(component, true); 110 } 111 setLayoutContainer(component, false); return true; 113 } 114 115 117 void registerComponent(LayoutComponent comp, boolean recursive) { 118 registerComponentImpl(comp); 119 if (recursive && comp.isLayoutContainer()) { 120 for (Iterator it=comp.getSubcomponents(); it.hasNext(); ) { 121 registerComponent((LayoutComponent)it.next(), recursive); 122 } 123 } 124 } 125 126 void registerComponentImpl(LayoutComponent comp) { 127 Object lc = idToComponents.put(comp.getId(), comp); 128 129 if (lc != comp) { 130 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.COMPONENT_REGISTERED); 132 ev.setComponent(comp); 133 addChange(ev); 134 fireEvent(ev); 135 } } 137 138 void unregisterComponent(LayoutComponent comp, boolean recursive) { 139 if (recursive && comp.isLayoutContainer()) { 140 for (Iterator it=comp.getSubcomponents(); it.hasNext(); ) { 141 unregisterComponent((LayoutComponent)it.next(), recursive); 142 } 143 } 144 removeComponentFromLinkSizedGroup(comp, HORIZONTAL); 145 removeComponentFromLinkSizedGroup(comp, VERTICAL); 146 unregisterComponentImpl(comp); 147 } 148 149 void unregisterComponentImpl(LayoutComponent comp) { 150 Object lc = idToComponents.remove(comp.getId()); 151 152 if (lc != null) { 153 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.COMPONENT_UNREGISTERED); 155 ev.setComponent(comp); 156 addChange(ev); 157 fireEvent(ev); 158 } } 160 161 void changeComponentId(LayoutComponent comp, String newId) { 162 unregisterComponentImpl(comp); 163 comp.setId(newId); 164 registerComponentImpl(comp); 165 } 166 167 void replaceComponent(LayoutComponent comp, LayoutComponent substComp) { 168 assert substComp.getParent() == null; 169 for (int i=0; i<DIM_COUNT; i++) { 170 LayoutInterval interval = comp.getLayoutInterval(i); 171 LayoutInterval substInt = substComp.getLayoutInterval(i); 172 assert substInt.getParent() == null; 173 setIntervalAlignment(substInt, interval.getRawAlignment()); 174 setIntervalSize(substInt, interval.getMinimumSize(), 175 interval.getPreferredSize(), interval.getMaximumSize()); 176 LayoutInterval parentInt = interval.getParent(); 177 if (parentInt != null) { 178 int index = removeInterval(interval); 179 addInterval(substInt, parentInt, index); 180 } 181 } 182 183 LayoutComponent parent = comp.getParent(); 184 if (parent != null) { 185 int index = removeComponentImpl(comp); 186 addComponentImpl(substComp, parent, index); 187 } 188 unregisterComponentImpl(comp); 189 registerComponentImpl(substComp); 190 } 191 192 Iterator getAllComponents() { 193 return idToComponents.values().iterator(); 194 } 195 196 public void addNewComponent(LayoutComponent component, LayoutComponent parent, LayoutComponent prototype) { 197 for (int i=0; i<DIM_COUNT; i++) { 198 LayoutInterval interval = component.getLayoutInterval(i); 199 if (parent != null) { 200 addInterval(interval, parent.getLayoutRoot(i), -1); 201 } 202 setIntervalAlignment(interval, DEFAULT); 203 if (prototype != null) { 205 LayoutInterval pInt = prototype.getLayoutInterval(i); 206 setIntervalSize(interval, interval.getMinimumSize(), pInt.getPreferredSize(), interval.getMaximumSize()); 207 } 208 } 209 addComponent(component, parent, -1); 210 } 211 212 void addComponent(LayoutComponent component, LayoutComponent parent, int index) { 215 addComponentImpl(component, parent, index); 216 registerComponent(component, true); 217 } 218 219 void addComponentImpl(LayoutComponent component, LayoutComponent parent, int index) { 220 assert component.getParent() == null; 221 222 if (parent != null) { 223 assert getLayoutComponent(parent.getId()) == parent; 224 index = parent.add(component, index); 225 } 226 else { 227 assert component.isLayoutContainer(); 228 } 229 230 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.COMPONENT_ADDED); 232 ev.setComponent(component, parent, index); 233 addChange(ev); 234 fireEvent(ev); 235 } 236 237 void removeComponent(LayoutComponent component) { 238 removeComponent(component, true); 239 } 240 241 void removeComponent(LayoutComponent component, boolean fromModel) { 245 removeComponentImpl(component); 246 if (fromModel && (getLayoutComponent(component.getId()) != null)) { 247 unregisterComponent(component, true); 248 } 249 } 250 251 int removeComponentImpl(LayoutComponent component) { 252 int index; 253 LayoutComponent parent = component.getParent(); 254 if (parent != null) { 255 index = parent.remove(component); 256 } else { 257 return -1; } 259 260 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.COMPONENT_REMOVED); 262 ev.setComponent(component, parent, index); 263 addChange(ev); 264 fireEvent(ev); 265 266 return index; 267 } 268 269 void removeComponentAndIntervals(LayoutComponent comp, boolean fromModel) { 270 boolean wasRoot = (comp.getParent() == null); 271 removeComponent(comp, fromModel); 272 if (!wasRoot) { 273 for (int i=0; i < DIM_COUNT; i++) { 274 LayoutInterval interval = comp.getLayoutInterval(i); 275 if (interval.getParent() != null) 276 removeInterval(interval); 277 } 278 } 279 } 280 281 void addInterval(LayoutInterval interval, LayoutInterval parent, int index) { 282 assert interval.getParent() == null; 283 284 index = parent.add(interval, index); 285 286 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_ADDED); 288 ev.setInterval(interval, parent, index); 289 addChange(ev); 290 fireEvent(ev); 291 } 292 293 int removeInterval(LayoutInterval interval) { 296 LayoutInterval parent = interval.getParent(); 297 int index = parent.remove(interval); 298 299 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_REMOVED); 301 ev.setInterval(interval, parent, index); 302 addChange(ev); 303 fireEvent(ev); 304 305 return index; 306 } 307 308 LayoutInterval removeInterval(LayoutInterval parent, int index) { 309 LayoutInterval interval = parent.remove(index); 310 311 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_REMOVED); 313 ev.setInterval(interval, parent, index); 314 addChange(ev); 315 fireEvent(ev); 316 317 return interval; 318 } 319 320 void changeIntervalAttribute(LayoutInterval interval, int attribute, boolean set) { 321 int oldAttributes = interval.getAttributes(); 322 if (set) { 323 interval.setAttribute(attribute); 324 } else { 325 interval.unsetAttribute(attribute); 326 } 327 int newAttributes = interval.getAttributes(); 328 329 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_ATTRIBUTES_CHANGED); 331 ev.setAttributes(interval, oldAttributes, newAttributes); 332 addChange(ev); 333 } 334 335 void setIntervalAlignment(LayoutInterval interval, int alignment) { 336 int oldAlignment = interval.getRawAlignment(); 337 interval.setAlignment(alignment); 338 339 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_ALIGNMENT_CHANGED); 341 ev.setAlignment(interval, oldAlignment, alignment); 342 addChange(ev); 343 } 344 345 void setGroupAlignment(LayoutInterval group, int alignment) { 346 int oldAlignment = group.getGroupAlignment(); 347 if (alignment == oldAlignment) { 348 return; 349 } 350 group.setGroupAlignment(alignment); 351 352 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.GROUP_ALIGNMENT_CHANGED); 354 ev.setAlignment(group, oldAlignment, alignment); 355 addChange(ev); 356 } 357 358 void setLayoutContainer(LayoutComponent component, boolean container) { 359 boolean oldContainer = component.isLayoutContainer(); 360 if (oldContainer != container) { 361 LayoutInterval[] roots = component.getLayoutRoots(); 362 component.setLayoutContainer(container, null); 363 364 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.CONTAINER_ATTR_CHANGED); 366 ev.setContainer(component, roots); 367 addChange(ev); 368 } 369 } 370 371 public void setIntervalSize(LayoutInterval interval, int min, int pref, int max) { 372 int oldMin = interval.getMinimumSize(); 373 int oldPref = interval.getPreferredSize(); 374 int oldMax = interval.getMaximumSize(); 375 if (min == oldMin && pref == oldPref && max == oldMax) { 376 return; } 378 interval.setSizes(min, pref, max); 379 if (interval.isComponent()) { 380 LayoutComponent comp = interval.getComponent(); 381 boolean horizontal = (interval == comp.getLayoutInterval(HORIZONTAL)); 382 if (oldMin != min) { 383 comp.firePropertyChange(horizontal ? PROP_HORIZONTAL_MIN_SIZE : PROP_VERTICAL_MIN_SIZE, 384 new Integer (oldMin), new Integer (min)); 385 } 386 if (oldPref != pref) { 387 comp.firePropertyChange(horizontal ? PROP_HORIZONTAL_PREF_SIZE : PROP_VERTICAL_PREF_SIZE, 388 new Integer (oldPref), new Integer (pref)); 389 } 390 if (oldMax != max) { 391 comp.firePropertyChange(horizontal ? PROP_HORIZONTAL_MAX_SIZE : PROP_VERTICAL_MAX_SIZE, 392 new Integer (oldMax), new Integer (max)); 393 } 394 } 395 396 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_SIZE_CHANGED); 398 ev.setSize(interval, oldMin, oldPref, oldMax, min, pref, max); 399 addChange(ev); 400 } 401 402 public void copyModelFrom(LayoutModel sourceModel, Map sourceToTargetIds, 403 String sourceContainerId, String targetContainerId) { 404 LayoutComponent sourceContainer = sourceModel.getLayoutComponent(sourceContainerId); 405 LayoutComponent targetContainer = getLayoutComponent(targetContainerId); 406 if (targetContainer == null) { 407 targetContainer = new LayoutComponent(targetContainerId, true); 408 addRootComponent(targetContainer); 409 } else if (!targetContainer.isLayoutContainer()) { 410 changeComponentToContainer(targetContainerId); 411 } 412 Iterator iter = sourceToTargetIds.entrySet().iterator(); 414 while (iter.hasNext()) { 415 Map.Entry entry = (Map.Entry)iter.next(); 416 String targetId = (String )entry.getValue(); 417 LayoutComponent targetLC = getLayoutComponent(targetId); 418 if (targetLC == null) { 419 String sourceId = (String )entry.getKey(); 420 LayoutComponent sourceLC = sourceModel.getLayoutComponent(sourceId); 421 targetLC = new LayoutComponent(targetId, sourceLC.isLayoutContainer()); 422 } 423 if (targetLC.getParent() == null) { 424 addComponent(targetLC, targetContainer, -1); 425 } 426 } 427 for (int dim=0; dim<DIM_COUNT; dim++) { 429 LayoutInterval sourceInterval = sourceContainer.getLayoutRoot(dim); 430 LayoutInterval targetInterval = targetContainer.getLayoutRoot(dim); 431 copyInterval(sourceInterval, targetInterval, sourceToTargetIds); 432 } 433 } 434 435 private void copyInterval(LayoutInterval sourceInterval, LayoutInterval targetInterval, Map sourceToTargetIds) { 436 Iterator iter = sourceInterval.getSubIntervals(); 437 while (iter.hasNext()) { 438 LayoutInterval sourceSub = (LayoutInterval)iter.next(); 439 LayoutInterval clone = null; 440 if (sourceSub.isComponent()) { 441 String compId = (String )sourceToTargetIds.get(sourceSub.getComponent().getId()); 442 LayoutComponent comp = getLayoutComponent(compId); 443 int dimension = (sourceSub == sourceSub.getComponent().getLayoutInterval(HORIZONTAL)) ? HORIZONTAL : VERTICAL; 444 clone = comp.getLayoutInterval(dimension); 445 } 446 LayoutInterval targetSub = LayoutInterval.cloneInterval(sourceSub, clone); 447 if (sourceSub.isGroup()) { 448 copyInterval(sourceSub, targetSub, sourceToTargetIds); 449 } 450 addInterval(targetSub, targetInterval, -1); 451 } 452 } 453 454 460 public void createModel(String containerId, Container cont, Map idToComponent) { 461 if (idToComponent.isEmpty()) return; 462 LayoutComponent lCont = getLayoutComponent(containerId); 463 assert (lCont != null); 464 Insets insets = new Insets(0, 0, 0, 0); 465 if (cont instanceof javax.swing.JComponent ) { 466 javax.swing.border.Border border = ((javax.swing.JComponent )cont).getBorder(); 467 if (border != null) { 468 insets = border.getBorderInsets(cont); 469 } 470 } 471 Map idToBounds = new HashMap(); 472 Iterator iter = idToComponent.entrySet().iterator(); 473 Rectangle notKnown = new Rectangle(); 474 while (iter.hasNext()) { 475 Map.Entry entry = (Map.Entry)iter.next(); 476 String id = (String )entry.getKey(); 477 Component component = (Component)entry.getValue(); 478 LayoutComponent lComp = getLayoutComponent(id); 479 if (lComp == null) { 480 lComp = new LayoutComponent(id, false); 481 } 482 addComponent(lComp, lCont, -1); 483 Rectangle bounds = component.getBounds(); 484 Dimension dim = component.getPreferredSize(); 485 if (bounds.equals(notKnown)) { bounds.setSize(dim); 487 } 488 bounds = new Rectangle(bounds.x - insets.left, bounds.y - insets.top, bounds.width, bounds.height); 489 idToBounds.put(id, bounds); 490 if (dim.width != bounds.width) { 491 LayoutInterval interval = lComp.getLayoutInterval(HORIZONTAL); 492 setIntervalSize(interval, interval.getMinimumSize(), bounds.width, interval.getMaximumSize()); 493 } 494 if (dim.height != bounds.height) { 495 LayoutInterval interval = lComp.getLayoutInterval(VERTICAL); 496 setIntervalSize(interval, interval.getMinimumSize(), bounds.height, interval.getMaximumSize()); 497 } 498 } 499 RegionInfo region = new RegionInfo(idToBounds); 500 region.calculateIntervals(); 501 addInterval(region.getInterval(HORIZONTAL), lCont.getLayoutRoot(HORIZONTAL), -1); 503 addInterval(region.getInterval(VERTICAL), lCont.getLayoutRoot(VERTICAL), -1); 504 } 505 506 public LayoutInterval[] createIntervalsFromBounds(LayoutRegion space, LayoutComponent[] components, LayoutRegion[] bounds) { 507 Map idToBounds = new HashMap(); 508 for (int i=0; i<components.length; i++) { 509 for (int dim=0; dim<DIM_COUNT; dim++) { 510 LayoutInterval interval = components[i].getLayoutInterval(dim); 511 setIntervalSize(interval, interval.getMinimumSize(), bounds[i].size(dim), interval.getMaximumSize()); 512 } 513 Rectangle compBounds = new Rectangle( 514 bounds[i].positions[HORIZONTAL][LEADING]-space.positions[HORIZONTAL][LEADING], 515 bounds[i].positions[VERTICAL][LEADING]-space.positions[VERTICAL][LEADING], 516 bounds[i].size(HORIZONTAL), 517 bounds[i].size(VERTICAL)); 518 idToBounds.put(components[i].getId(), compBounds); 519 } 520 RegionInfo region = new RegionInfo(idToBounds); 521 region.calculateIntervals(); 522 LayoutInterval[] result = new LayoutInterval[DIM_COUNT]; 523 for (int dim=0; dim<DIM_COUNT; dim++) { 524 result[dim] = region.getInterval(dim); 525 } 526 return result; 527 } 528 529 private class RegionInfo { 530 private LayoutInterval horizontal = null; 531 private LayoutInterval vertical = null; 532 private Map idToBounds; 533 private int minx; 534 private int maxx; 535 private int miny; 536 private int maxy; 537 private int dimension; 538 539 public RegionInfo(Map idToBounds) { 540 this.idToBounds = idToBounds; 541 this.dimension = -1; 542 minx = miny = 0; 543 updateRegionBounds(); 544 } 545 546 private RegionInfo(Map idToBounds, int dimension) { 547 this.idToBounds = idToBounds; 548 this.dimension = dimension; 549 minx = miny = Short.MAX_VALUE; 550 updateRegionBounds(); 551 } 552 553 private void updateRegionBounds() { 554 maxy = maxx = Short.MIN_VALUE; 555 Iterator iter = idToBounds.values().iterator(); 556 while (iter.hasNext()) { 557 Rectangle bounds = (Rectangle)iter.next(); 558 minx = Math.min(minx, bounds.x); 559 miny = Math.min(miny, bounds.y); 560 maxx = Math.max(maxx, bounds.x + bounds.width); 561 maxy = Math.max(maxy, bounds.y + bounds.height); 562 } 563 } 564 565 public void calculateIntervals() { 566 if (idToBounds.size() == 1) { 567 String id = (String )idToBounds.keySet().iterator().next(); 568 Rectangle bounds = (Rectangle)idToBounds.get(id); 569 LayoutComponent comp = getLayoutComponent(id); 570 horizontal = comp.getLayoutInterval(HORIZONTAL); 571 horizontal = prefixByGap(horizontal, bounds.x - minx); 572 vertical = comp.getLayoutInterval(VERTICAL); 573 vertical = prefixByGap(vertical, bounds.y - miny); 574 return; 575 } 576 int effDim = -1; 577 List parts = null; 578 Map removedIdToBounds = null; 579 do { 580 boolean remove = ((dimension == -1) && (effDim == HORIZONTAL)) 581 || ((dimension != -1) && (effDim != -1)); 582 if (remove) { 583 effDim = -1; 584 } 585 if (dimension == -1) { 586 switch (effDim) { 587 case -1: effDim = VERTICAL; break; 588 case VERTICAL: effDim = HORIZONTAL; break; 589 case HORIZONTAL: remove = true; 590 } 591 } else { 592 effDim = dimension; 593 } 594 if (remove) { String id = (String )idToBounds.keySet().iterator().next(); 596 Rectangle bounds = (Rectangle)idToBounds.remove(id); 597 if (removedIdToBounds == null) { 598 removedIdToBounds = new HashMap(); 599 } 600 removedIdToBounds.put(id, bounds); 601 } 602 Set cutSet = createPossibleCuts(effDim); 603 parts = cutIntoParts(cutSet, effDim); 604 } while (!idToBounds.isEmpty() && parts.isEmpty()); 605 dimension = effDim; 606 List regions = new LinkedList(); 607 Iterator iter = parts.iterator(); 608 while (iter.hasNext()) { 609 Map part = (Map)iter.next(); 610 RegionInfo region = new RegionInfo(part, (dimension == HORIZONTAL) ? VERTICAL : HORIZONTAL); 611 region.calculateIntervals(); 612 regions.add(region); 613 } 614 mergeSubRegions(regions, dimension); 615 if (removedIdToBounds != null) { 616 for (int dim = HORIZONTAL; dim <= VERTICAL; dim++) { 617 iter = removedIdToBounds.entrySet().iterator(); 618 LayoutInterval parent = (dim == HORIZONTAL) ? horizontal : vertical; 619 if (!parent.isParallel()) { 620 LayoutInterval parGroup = new LayoutInterval(PARALLEL); 621 addInterval(parent, parGroup, -1); 622 if (dim == HORIZONTAL) { 623 horizontal = parGroup; 624 } else { 625 vertical = parGroup; 626 } 627 parent = parGroup; 628 } 629 while (iter.hasNext()) { 630 Map.Entry entry = (Map.Entry)iter.next(); 631 String id = (String )entry.getKey(); 632 Rectangle bounds = (Rectangle)entry.getValue(); 633 LayoutComponent comp = getLayoutComponent(id); 634 LayoutInterval interval = comp.getLayoutInterval(dim); 635 int gap = (dim == HORIZONTAL) ? bounds.x - minx : bounds.y - miny; 636 interval = prefixByGap(interval, gap); 637 addInterval(interval, parent, -1); 638 } 639 } 640 } 641 } 642 643 private SortedSet createPossibleCuts(int dimension) { 644 SortedSet cutSet = new TreeSet(); 645 Iterator iter = idToBounds.keySet().iterator(); 646 while (iter.hasNext()) { 647 String id = (String )iter.next(); 648 Rectangle bounds = (Rectangle)idToBounds.get(id); 649 int leading = (dimension == HORIZONTAL) ? bounds.x : bounds.y; 651 cutSet.add(new Integer (leading)); 652 } 653 cutSet.add(new Integer ((dimension == HORIZONTAL) ? maxx : maxy)); 654 return cutSet; 655 } 656 657 private List cutIntoParts(Set cutSet, int dimension) { 658 List parts = new LinkedList(); 659 Iterator iter = cutSet.iterator(); 660 while (iter.hasNext()) { 661 Integer cutInt = (Integer )iter.next(); 662 int cut = cutInt.intValue(); 663 boolean isCut = true; 664 Map preIdToBounds = new HashMap(); 665 Map postIdToBounds = new HashMap(); 666 Iterator it = idToBounds.entrySet().iterator(); 667 while (isCut && it.hasNext()) { 668 Map.Entry entry = (Map.Entry)it.next(); 669 String id = (String )entry.getKey(); 670 Rectangle bounds = (Rectangle)entry.getValue(); 671 int leading = (dimension == HORIZONTAL) ? bounds.x : bounds.y; 672 int trailing = leading + ((dimension == HORIZONTAL) ? bounds.width : bounds.height); 673 if (leading >= cut) { 674 postIdToBounds.put(id, bounds); 675 } else if (trailing <= cut) { 676 preIdToBounds.put(id, bounds); 677 } else { 678 isCut = false; 679 } 680 } 681 if (isCut && !preIdToBounds.isEmpty() 682 && (!parts.isEmpty() || (preIdToBounds.size() != idToBounds.size()))) { 684 idToBounds.keySet().removeAll(preIdToBounds.keySet()); 685 parts.add(preIdToBounds); 686 } 687 } 688 return parts; 689 } 690 691 private void mergeSubRegions(List regions, int dimension) { 692 if (regions.size() == 0) { 693 horizontal = new LayoutInterval(PARALLEL); 694 vertical = new LayoutInterval(PARALLEL); 695 return; 696 } 697 LayoutInterval seqGroup = new LayoutInterval(SEQUENTIAL); 698 LayoutInterval parGroup = new LayoutInterval(PARALLEL); 699 int lastSeqTrailing = (dimension == HORIZONTAL) ? minx : miny; 700 Iterator iter = regions.iterator(); 701 while (iter.hasNext()) { 702 RegionInfo region = (RegionInfo)iter.next(); 703 LayoutInterval seqInterval; 704 LayoutInterval parInterval; 705 int seqGap; 706 int parGap; 707 if (dimension == HORIZONTAL) { 708 seqInterval = region.horizontal; 709 parInterval = region.vertical; 710 parGap = region.miny - miny; 711 seqGap = region.minx - lastSeqTrailing; 712 lastSeqTrailing = region.maxx; 713 } else { 714 seqInterval = region.vertical; 715 parInterval = region.horizontal; 716 parGap = region.minx - minx; 717 seqGap = region.miny - lastSeqTrailing; 718 lastSeqTrailing = region.maxy; 719 } 720 if (seqGap > 0) { 722 LayoutInterval gap = new LayoutInterval(SINGLE); 723 gap.setSize(seqGap); 724 addInterval(gap, seqGroup, -1); 725 } 726 addInterval(seqInterval, seqGroup, -1); 727 parInterval = prefixByGap(parInterval, parGap); 728 addInterval(parInterval, parGroup, -1); 729 } 730 if (dimension == HORIZONTAL) { 731 horizontal = seqGroup; 732 vertical = parGroup; 733 } else { 734 horizontal = parGroup; 735 vertical = seqGroup; 736 } 737 } 738 739 private LayoutInterval prefixByGap(LayoutInterval interval, int size) { 740 if (size > 0) { 741 LayoutInterval gap = new LayoutInterval(SINGLE); 742 gap.setSize(size); 743 if (interval.isSequential()) { 744 addInterval(gap, interval, 0); 745 } else { 746 LayoutInterval group = new LayoutInterval(SEQUENTIAL); 747 addInterval(gap, group, -1); 748 addInterval(interval, group, -1); 749 interval = group; 750 } 751 } 752 return interval; 753 } 754 755 public LayoutInterval getInterval(int dimension) { 756 return (dimension == HORIZONTAL) ? horizontal : vertical; 757 } 758 759 } 760 761 764 void addListener(Listener l) { 765 if (listeners == null) { 766 listeners = new ArrayList(); 767 } 768 else { 769 listeners.remove(l); 770 } 771 listeners.add(l); 772 } 773 774 void removeListener(Listener l) { 775 if (listeners != null) { 776 listeners.remove(l); 777 } 778 } 779 780 private void fireEvent(LayoutEvent event) { 781 if (listeners != null && listeners.size() > 0) { 782 Iterator it = ((List )listeners.clone()).iterator(); 783 while (it.hasNext()) { 784 ((Listener)it.next()).layoutChanged(event); 785 } 786 } 787 } 788 789 792 interface Listener { 793 void layoutChanged(LayoutEvent ev); 794 } 795 796 799 public boolean isChangeRecording() { 800 return recordingChanges; 801 } 802 803 public void setChangeRecording(boolean record) { 804 recordingChanges = record; 805 } 806 807 boolean isUndoRedoInProgress() { 808 return undoRedoInProgress; 809 } 810 811 public Object getChangeMark() { 812 return new Integer (changeMark); 813 } 814 815 public void endUndoableEdit() { 816 if (lastUndoableEdit != null) { 817 lastUndoableEdit.endMark = getChangeMark(); 818 lastUndoableEdit = null; 819 } 820 } 821 822 public boolean isUndoableEditInProgress() { 823 return (lastUndoableEdit != null); 824 } 825 826 public UndoableEdit getUndoableEdit() { 827 if (recordingChanges && !undoRedoInProgress) { 828 LayoutUndoableEdit undoEdit = new LayoutUndoableEdit(); 829 undoEdit.startMark = getChangeMark(); 830 endUndoableEdit(); 831 lastUndoableEdit = undoEdit; 832 return undoEdit; 833 } 834 return null; 835 } 836 837 private void addChange(LayoutEvent change) { 838 if (recordingChanges && !undoRedoInProgress) { 839 redoMap.clear(); 840 if (undoMap.size() == 0) 841 oldestMark = changeMark; 842 843 undoMap.put(new Integer (changeMark++), change); 844 845 while (undoMap.size() > changeCountHardLimit) { 846 undoMap.remove(new Integer (oldestMark++)); 847 } 848 } 849 } 850 851 boolean undo(Object startMark, Object endMark) { 852 assert !undoRedoInProgress; 853 if (!undoMap.containsKey(startMark)) { 854 return false; } 856 857 int start = ((Integer )startMark).intValue(); 858 int end = ((Integer )endMark).intValue(); 859 undoRedoInProgress = true; 860 861 while (end > start) { 862 Object key = new Integer (--end); 863 LayoutEvent change = (LayoutEvent) undoMap.remove(key); 864 if (change != null) { 865 change.undo(); 866 redoMap.put(key, change); 867 } 868 } 869 870 undoRedoInProgress = false; 871 return true; 872 } 873 874 boolean redo(Object startMark, Object endMark) { 875 assert !undoRedoInProgress; 876 if (!redoMap.containsKey(startMark)) { 877 return false; } 879 880 int start = ((Integer )startMark).intValue(); 881 int end = ((Integer )endMark).intValue(); 882 undoRedoInProgress = true; 883 884 while (start < end) { 885 Object key = new Integer (start++); 886 LayoutEvent change = (LayoutEvent) redoMap.remove(key); 887 if (change != null) { 888 change.redo(); 889 undoMap.put(key, change); 890 } 891 } 892 893 undoRedoInProgress = false; 894 return true; 895 } 896 897 void releaseChanges(Object fromMark, Object toMark) { 898 int m1 = ((Integer )fromMark).intValue(); 899 int m2 = ((Integer )toMark).intValue(); 900 901 while (m1 < m2) { 902 Object m = new Integer (m1); 903 undoMap.remove(m); 904 redoMap.remove(m); 905 m1++; 906 } 907 } 908 909 912 private class LayoutUndoableEdit extends AbstractUndoableEdit { 913 private Object startMark; 914 private Object endMark; 915 916 public void undo() throws CannotUndoException { 917 super.undo(); 918 if (endMark == null) { 919 assert lastUndoableEdit == this; 920 endMark = getChangeMark(); 921 lastUndoableEdit = null; 922 } 923 LayoutModel.this.undo(startMark, endMark); 924 } 925 926 public void redo() throws CannotRedoException { 927 super.redo(); 928 LayoutModel.this.redo(startMark, endMark); 929 } 930 931 public String getUndoPresentationName() { 932 return ""; } 934 public String getRedoPresentationName() { 935 return ""; } 937 938 public void die() { 939 releaseChanges(startMark, endMark != null ? endMark : getChangeMark()); 940 } 941 } 942 943 948 public String dump(final Map idToNameMap) { 949 Set roots = new TreeSet(new Comparator() { 950 public int compare(Object o1, Object o2) { 953 if (o1 == o2) 954 return 0; 955 LayoutComponent lc1 = (LayoutComponent) o1; 956 LayoutComponent lc2 = (LayoutComponent) o2; 957 if (lc1.isParentOf(lc2)) 959 return -1; 960 if (lc2.isParentOf(lc1)) 961 return 1; 962 LayoutComponent parent = LayoutComponent.getCommonParent(lc1, lc2); 964 while (lc1.getParent() != parent) 965 lc1 = lc1.getParent(); 966 while (lc2.getParent() != parent) 967 lc2 = lc2.getParent(); 968 if (parent != null) { return parent.indexOf(lc1) < parent.indexOf(lc2) ? -1 : 1; 970 } 971 else { String id1 = lc1.getId(); 973 String id2 = lc2.getId(); 974 if (idToNameMap != null) { 975 id1 = (String ) idToNameMap.get(id1); 976 id2 = (String ) idToNameMap.get(id2); 977 if (id1 == null) { 978 return -1; 979 } 980 if (id2 == null) { 981 return 1; 982 } 983 } 984 return id1.compareTo(id2); 985 } 986 } 987 }); 988 Iterator iter = idToComponents.entrySet().iterator(); 989 while (iter.hasNext()) { 990 Map.Entry entry = (Map.Entry)iter.next(); 991 LayoutComponent comp = (LayoutComponent)entry.getValue(); 992 if (comp.isLayoutContainer()) { 993 roots.add(comp); 994 } 995 } 996 StringBuffer sb = new StringBuffer (); 997 sb.append("<LayoutModel>\n"); Iterator rootIter = roots.iterator(); 999 while (rootIter.hasNext()) { 1000 LayoutComponent root = (LayoutComponent)rootIter.next(); 1001 String rootId = root.getId(); 1002 if (idToNameMap != null) { 1003 rootId = (String ) idToNameMap.get(rootId); 1004 } 1005 if (rootId != null) 1006 sb.append(" <Root id=\""+rootId+"\">\n"); else 1008 sb.append(" <Root>\n"); sb.append(dumpLayout(2, root, idToNameMap, true)); 1010 sb.append(" </Root>\n"); } 1012 sb.append("</LayoutModel>\n"); return sb.toString(); 1014 } 1015 1016 1022 public String dump(LayoutInterval interval, int dimension) { 1023 return new LayoutPersistenceManager(this).saveIntervalLayout(2, interval, dimension); 1024 } 1025 1026 1035 public String dumpLayout(int indent, LayoutComponent root, Map idToNameMap, boolean humanReadable) { 1036 return new LayoutPersistenceManager(this).saveLayout(indent, root, idToNameMap, humanReadable); 1037 } 1038 1039 1046 public void loadModel(String rootId, org.w3c.dom.NodeList dimLayoutList, Map nameToIdMap) 1047 throws java.io.IOException 1048 { 1049 new LayoutPersistenceManager(this).loadModel(rootId, dimLayoutList, nameToIdMap); 1050 } 1051 1052 1058 public boolean wasCorrected() { 1059 return corrected; 1060 } 1061 1062 void setCorrected() { 1063 corrected = true; 1064 } 1065 1066 1069 1070 private Map linkSizeGroupsH = new HashMap(); 1072 private Map linkSizeGroupsV = new HashMap(); 1073 1074 private int maxLinkGroupId = 0; 1075 1076 void addComponentToLinkSizedGroup(int groupId, String compId, int dimension) { 1077 1078 if (NOT_EXPLICITLY_DEFINED == groupId) { return; 1080 } 1081 if (maxLinkGroupId < groupId) { 1082 maxLinkGroupId=groupId; 1083 } 1084 Integer groupIdInt = new Integer (groupId); 1085 Map linkSizeGroups = (dimension == HORIZONTAL) ? linkSizeGroupsH : linkSizeGroupsV; 1086 List l = (List )linkSizeGroups.get(groupIdInt); 1087 if ((l != null) && (l.contains(compId) || !sameContainer(compId, (String )l.get(0)))) { 1088 return; 1089 } 1090 addComponentToLinkSizedGroupImpl(groupId, compId, dimension); 1091 } 1092 1093 void addComponentToLinkSizedGroupImpl(int groupId, String compId, int dimension) { 1094 LayoutComponent lc = getLayoutComponent(compId); 1095 Integer groupIdInt = new Integer (groupId); 1096 Map linkSizeGroups = (dimension == HORIZONTAL) ? linkSizeGroupsH : linkSizeGroupsV; 1097 List l = (List )linkSizeGroups.get(groupIdInt); 1098 if (l != null) { 1099 l.add(lc.getId()); 1100 } else { 1101 l = new ArrayList(); 1102 l.add(lc.getId()); 1103 linkSizeGroups.put(groupIdInt, l); 1104 } 1105 1106 int oldLinkSizeId = lc.getLinkSizeId(dimension); 1107 lc.setLinkSizeId(groupId, dimension); 1108 1109 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_LINKSIZE_CHANGED); 1111 ev.setLinkSizeGroup(lc, oldLinkSizeId, groupId, dimension); 1112 addChange(ev); 1113 fireEvent(ev); 1114 } 1115 1116 private boolean sameContainer(String compId1, String compId2) { 1117 LayoutComponent lc1 = getLayoutComponent(compId1); 1118 LayoutComponent lc2 = getLayoutComponent(compId2); 1119 return lc1.getParent().equals(lc2.getParent()); 1120 } 1121 1122 void removeComponentFromLinkSizedGroup(LayoutComponent comp, int dimension) { 1123 1124 if (comp == null) return; 1125 1126 int linkId = comp.getLinkSizeId(dimension); 1127 if (linkId != NOT_EXPLICITLY_DEFINED) { 1128 1129 Map map = (dimension == HORIZONTAL) ? linkSizeGroupsH : linkSizeGroupsV; 1130 Integer linkIdInt = new Integer (linkId); 1131 1132 List l = null; 1133 l = (List )map.get(linkIdInt); 1134 l.remove(comp.getId()); 1135 comp.setLinkSizeId(NOT_EXPLICITLY_DEFINED, dimension); 1136 1137 if (l.size() == 1) { 1138 LayoutComponent lc = getLayoutComponent((String )l.get(0)); 1139 int oldLinkSizeId = lc.getLinkSizeId(dimension); 1140 lc.setLinkSizeId(NOT_EXPLICITLY_DEFINED, dimension); 1141 map.remove(linkIdInt); 1142 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_LINKSIZE_CHANGED); 1144 ev.setLinkSizeGroup(lc, oldLinkSizeId, NOT_EXPLICITLY_DEFINED, dimension); 1145 addChange(ev); 1146 fireEvent(ev); 1147 } 1148 1149 if (l.size() == 0) { 1150 map.remove(linkIdInt); 1151 } 1152 1153 LayoutEvent ev = new LayoutEvent(this, LayoutEvent.INTERVAL_LINKSIZE_CHANGED); 1155 ev.setLinkSizeGroup(comp, linkId, NOT_EXPLICITLY_DEFINED, dimension); 1156 addChange(ev); 1157 fireEvent(ev); 1158 } 1159 } 1160 1161 1166 public int areComponentsLinkSized(List components, int dimension) { 1167 1168 if (components.size() == 1) { 1169 String id = (String )components.get(0); 1170 boolean retVal = (getLayoutComponent(id).isLinkSized(dimension)); 1171 return retVal ? TRUE : FALSE; 1172 } 1173 1174 Iterator i = components.iterator(); 1175 boolean someUnlinkedPresent = false; 1176 List idsFound = new ArrayList(); 1177 1178 while (i.hasNext()) { 1179 String cid = (String )i.next(); 1180 LayoutComponent lc = getLayoutComponent(cid); 1181 Integer linkSizeId = new Integer (lc.getLinkSizeId(dimension)); 1182 if (!idsFound.contains(linkSizeId)) { 1183 idsFound.add(linkSizeId); 1184 } 1185 if (idsFound.size() > 2) { return INVALID; 1187 } 1188 } 1189 if (idsFound.size() == 1) { 1190 if (idsFound.contains(new Integer (NOT_EXPLICITLY_DEFINED))) { 1191 return FALSE; 1192 } 1193 return TRUE; 1194 } 1195 if (idsFound.contains(new Integer (NOT_EXPLICITLY_DEFINED))) { return FALSE; 1197 } else { 1198 return INVALID; 1199 } 1200 } 1201 1202 Map getLinkSizeGroups(int dimension) { 1203 if (HORIZONTAL == dimension) { 1204 return linkSizeGroupsH; 1205 } 1206 if (VERTICAL == dimension) { 1207 return linkSizeGroupsV; 1208 } 1209 return null; } 1211 1212 public void unsetSameSize(List components, int dimension) { 1213 Iterator i = components.iterator(); 1214 while (i.hasNext()) { 1215 String cid = (String )i.next(); 1216 LayoutComponent lc = getLayoutComponent(cid); 1217 removeComponentFromLinkSizedGroup(lc, dimension); 1218 } 1219 } 1220 1221 public void setSameSize(List components, int dimension) { 1222 Iterator i = components.iterator(); 1223 int groupId = findGroupId(components, dimension); 1224 1225 while (i.hasNext()) { 1226 String cid = (String )i.next(); 1227 LayoutComponent lc = getLayoutComponent(cid); 1228 addComponentToLinkSizedGroup(groupId, lc.getId(), dimension); 1229 } 1230 } 1231 1232 private int findGroupId(List components, int dimension) { 1233 Iterator i = components.iterator(); 1234 while (i.hasNext()) { 1235 String cid = (String )i.next(); 1236 LayoutComponent lc = getLayoutComponent(cid); 1237 if (lc.isLinkSized(dimension)) { 1238 return lc.getLinkSizeId(dimension); 1239 } 1240 } 1241 return ++maxLinkGroupId; 1242 } 1243} 1244 | Popular Tags |