1 6 7 package org.netbeans.swing.gridsplit; 8 9 import java.awt.Component ; 10 import java.awt.Dimension ; 11 import java.awt.Point ; 12 import java.awt.Rectangle ; 13 import java.util.Iterator ; 14 import java.util.List ; 15 import java.util.ArrayList ; 16 import org.apache.xpath.axes.IteratorPool; 17 18 22 public class GridSplitCell { 23 24 public static final int NORTH = 1; 25 public static final int SOUTH = 2; 26 public static final int WEST = 4; 27 public static final int EAST = 8; 28 29 public static final int NO_SPLITTER = 0; 30 public static final int HORIZONTAL_SPLITTER = NORTH + SOUTH; 31 public static final int VERTICAL_SPLITTER = WEST + EAST; 32 33 34 private GridSplitCell parent; 36 private List children = new ArrayList (); 38 private int splitterOrientation = NO_SPLITTER; 40 private Component component; 42 private double resizeWeight = 0.0; 44 private double normalizedResizeWeight = 0.0; 46 private boolean collapsed = false; 48 private boolean isHidden = false; 50 private Dimension dimension = new Dimension (0,0); 52 private Point location = new Point (0,0); 54 55 GridSplitCell( Component c, double resizeWeight ) { 56 this( c, resizeWeight, new Dimension (0,0) ); 57 } 58 59 GridSplitCell( Component c, double resizeWeight, Dimension initialDimension ) { 60 this.component = c; 61 this.resizeWeight = resizeWeight; 62 this.dimension.width = initialDimension.width; 63 this.dimension.height = initialDimension.height; 64 } 65 66 69 GridSplitCell( GridSplitCell src ) { 70 copy( src, this ); 71 this.parent = src.parent; 72 } 73 74 public GridSplitCell getParent() { 75 return parent; 76 } 77 78 void setParent( GridSplitCell newParent ) { 79 this.parent = newParent; 80 } 81 82 85 public int count() { 86 return children.size(); 87 } 88 89 92 public int countVisibleCells() { 93 return getVisibleCells().size(); 95 } 96 97 100 public GridSplitCell cellAt( int index ) { 101 return (GridSplitCell) children.get( index ); 102 } 103 104 107 public boolean isRootCell() { 108 return null == getParent(); 109 } 110 111 119 public GridSplitCell addToSide( Component compToAdd, int side, double initialSize, double resizeWeight ) { 120 GridSplitCell newChild = new GridSplitCell( compToAdd, resizeWeight ); 121 GridSplitCell owner; 122 GridSplitCell neighbor; 123 if( isRootCell() ) { 124 owner = this; 127 if( isSplitterSide( side ) ) { 128 if( side == NORTH || side == WEST ) { 131 neighbor = cellAt( 0 ); 132 } else { 133 neighbor = cellAt( count()-1 ); 134 } 135 } else { 136 owner = this; 138 neighbor = split(); 139 } 140 } else { 141 owner = getParent(); 142 if( owner.isSplitterSide( side ) ) { 143 neighbor = this; 145 } else { 146 owner = this; 148 neighbor = split(); 149 } 150 } 151 owner.addToSide( neighbor, newChild, side, initialSize ); 152 return newChild; 153 } 154 155 158 private GridSplitCell split() { 159 GridSplitCell child = new GridSplitCell( this ); 160 161 children.clear(); 162 children.add( child ); 163 child.setParent( this ); 164 component = null; 165 166 return child; 167 } 168 169 172 private void addToSide( GridSplitCell neighbor, GridSplitCell newChild, int side, double initialSize ) { 173 Dimension parentSize = getDimension( 0 ); 174 splitterOrientation = (side == NORTH || side == SOUTH) ? VERTICAL_SPLITTER : HORIZONTAL_SPLITTER; 175 int insertIndex = children.indexOf( neighbor ); 176 assert insertIndex >= 0; 177 if( side == SOUTH || side == EAST ) 178 insertIndex++; 179 if( insertIndex > children.size() ) 180 children.add( newChild ); 181 else 182 children.add( insertIndex, newChild ); 183 if( isHorizontalSplitter() ) { 184 newChild.dimension = new Dimension ( (int)(parentSize.width*initialSize), parentSize.height ); 185 } else { 186 newChild.dimension = new Dimension ( parentSize.width, (int)(parentSize.height*initialSize) ); 187 } 188 newChild.setParent( this ); 189 } 190 191 194 boolean isSplitterSide( int side ) { 195 return (isHorizontalSplitter() && (side == WEST || side == EAST)) 196 || (isVerticalSplitter() && (side == SOUTH || side == NORTH)); 197 } 198 199 203 public void remove( GridSplitCell child ) { 204 assert children.contains( child ); 205 206 children.remove( child ); 207 if( children.size() == 1 ) { 208 GridSplitCell orphan = cellAt( 0 ); 209 210 if( null != getParent() ) { 211 getParent().replace( this, orphan ); 213 } else { 214 copy( orphan, this ); 216 } 217 } 218 } 219 220 223 private void replace( GridSplitCell origCell, GridSplitCell newCell ) { 224 assert children.contains( origCell ); 225 int index = children.indexOf( origCell ); 226 if( newCell.isSplit() && newCell.splitterOrientation == splitterOrientation ) { 227 children.addAll( index, newCell.children ); 228 for( Iterator i=newCell.children.iterator(); i.hasNext(); ) { 229 GridSplitCell c = (GridSplitCell)i.next(); 230 c.setParent( this ); 231 } 232 children.remove( origCell ); 233 } else { 234 children.set( index, newCell ); 235 newCell.setParent( this ); 236 } 237 } 238 239 242 public boolean isSplit() { 243 return null == component || children.size() == 1; 244 } 245 246 public int getSplitOrientation() { 247 return splitterOrientation; 248 } 249 250 public boolean isHorizontalSplitter() { 251 return splitterOrientation == HORIZONTAL_SPLITTER; 252 } 253 254 public boolean isVerticalSplitter() { 255 return splitterOrientation == VERTICAL_SPLITTER; 256 } 257 258 public Component getComponent() { 259 return component; 260 } 261 262 267 public Dimension getMinimumSize( int dividerSize ) { 268 if( isCollapsed() || isHidden() ) { 269 return new Dimension ( 0, 0 ); 270 } else { 271 if( isSplit() ) { 272 return calculateMinimumSize( dividerSize ); 273 } else { 274 return component.getMinimumSize(); 275 } 276 } 277 } 278 279 282 private Dimension calculateMinimumSize( int dividerSize ) { 283 Dimension minSize = new Dimension ( 0, 0 ); 284 ArrayList visibleCells = getVisibleCells(); 285 for( Iterator i=visibleCells.iterator(); i.hasNext(); ) { 286 GridSplitCell child = (GridSplitCell)i.next(); 287 288 Dimension childMinSize = child.getMinimumSize( dividerSize ); 289 290 if( isHorizontalSplitter() ) { 291 minSize.width += childMinSize.width; 292 if( childMinSize.height > minSize.height ) 293 minSize.height = childMinSize.height; 294 } else { 295 minSize.height += childMinSize.height; 296 if( childMinSize.width > minSize.width ) 297 minSize.width = childMinSize.width; 298 } 299 } 300 if( isHorizontalSplitter() ) { 301 minSize.width += (visibleCells.size()-1)*dividerSize; 302 } else { 303 minSize.height += (visibleCells.size()-1)*dividerSize; 304 } 305 306 return minSize; 307 } 308 309 312 ArrayList getVisibleCells() { 313 ArrayList res = new ArrayList ( children.size() ); 314 for( Iterator i=children.iterator(); i.hasNext(); ) { 315 GridSplitCell child = (GridSplitCell)i.next(); 316 if( child.isHidden() ) 317 continue; 318 res.add( child ); 319 } 320 return res; 321 } 322 323 326 ArrayList getResizeableCells() { 327 ArrayList res = new ArrayList ( children.size() ); 328 for( Iterator i=children.iterator(); i.hasNext(); ) { 329 GridSplitCell child = (GridSplitCell)i.next(); 330 if( child.isHidden() || child.isCollapsed() ) 331 continue; 332 res.add( child ); 333 } 334 return res; 335 } 336 337 340 ArrayList getResizeHungryCells() { 341 ArrayList res = new ArrayList ( children.size() ); 342 for( Iterator i=children.iterator(); i.hasNext(); ) { 343 GridSplitCell child = (GridSplitCell)i.next(); 344 if( child.isHidden() || child.isCollapsed() || child.resizeWeight == 0.0 ) 345 continue; 346 res.add( child ); 347 } 348 return res; 349 } 350 351 354 void resize( int width, int height, int dividerSize ) { 355 if( isSplit() ) { 356 357 int currentSize = getSize( this, dividerSize ); 359 int newNetSize = (isHorizontalSplitter() ? width : height); 360 int delta = newNetSize - currentSize; 361 363 if( delta > 0 ) { 364 366 grow( delta, dividerSize ); 367 368 } else if( delta < 0 ) { 369 370 delta = shrink( delta, dividerSize ); 371 372 if( delta > 0 ) { 373 newNetSize -= delta; 376 if( isHorizontalSplitter() ) 377 width -= delta; 378 else 379 height -= delta; 380 } 381 } 382 383 int totalSize = 0; 385 ArrayList visibleCells = getVisibleCells(); 386 for( int i=0; i<visibleCells.size(); i++ ) { 387 GridSplitCell child = (GridSplitCell)visibleCells.get( i ); 388 int childWidth = isHorizontalSplitter() ? child.getSize( this, dividerSize ) : width; 389 int childHeight = isHorizontalSplitter() ? height : child.getSize( this, dividerSize ); 390 391 if( isHorizontalSplitter() ) { 392 totalSize += childWidth; 393 if( i == visibleCells.size()-1 && totalSize < newNetSize ) { 394 System.out.println( "Extra width: " + (newNetSize - totalSize)); 397 childWidth += newNetSize - totalSize; 398 } else { 400 totalSize += dividerSize; 401 } 402 } else { 403 totalSize += childHeight; 404 if( i == visibleCells.size()-1 && totalSize < newNetSize ) { 405 System.out.println( "Extra height: " + (newNetSize - totalSize)); 408 childHeight += newNetSize - totalSize; 409 } else { 411 totalSize += dividerSize; 412 } 413 } 414 child.resize( childWidth, childHeight, dividerSize ); 415 } 416 if( !(isCollapsed() || isHidden()) ) { 417 if( isHorizontalSplitter() ) 418 setDimension( new Dimension ( newNetSize, height ) ); 419 else 420 setDimension( new Dimension ( width, newNetSize ) ); 421 } 422 } else { 423 setDimension( new Dimension ( width, height ) ); 424 } 425 } 426 427 430 private void grow( int delta, int dividerSize ) { 431 ArrayList hungryCells = getResizeHungryCells(); 433 434 if( !hungryCells.isEmpty() ) { 436 normalizeResizeWeights( hungryCells ); 438 distributeDelta( delta, hungryCells, dividerSize ); 439 } else { 440 ArrayList resizeableCells = getResizeableCells(); 442 normalizeResizeWeights( resizeableCells ); 443 distributeDelta( delta, resizeableCells, dividerSize ); 444 } 445 } 446 447 453 private int shrink( int negativeDelta, int dividerSize ) { 454 int delta = -negativeDelta; 455 456 ArrayList hungryCells = getResizeHungryCells(); 458 459 int resizeArea = calculateShrinkableArea( hungryCells, dividerSize ); 461 if( resizeArea >= delta ) { 462 resizeArea = delta; 463 delta = 0; 464 } else { 465 delta -= resizeArea; 466 } 467 if( resizeArea > 0 ) { 468 distributeDelta( -resizeArea, hungryCells, dividerSize ); 470 } 471 472 if( delta > 0 ) { 473 ArrayList resizeableCells = getResizeableCells(); 476 477 resizeArea = calculateShrinkableArea( resizeableCells, dividerSize ); 478 if( resizeArea >= delta ) { 479 resizeArea = delta; 480 delta = 0; 481 } else { 482 delta -= resizeArea; 483 } 484 if( resizeArea > 0 ) { 485 distributeDelta( -resizeArea, resizeableCells, dividerSize ); 486 } 487 } 488 return delta; 489 } 490 491 497 private int calculateShrinkableArea( ArrayList cells, int dividerSize ) { 498 int res = 0; 499 ArrayList nonShrinkable = new ArrayList ( cells.size() ); 500 for( int i=0; i<cells.size(); i++ ) { 501 GridSplitCell c = (GridSplitCell)cells.get( i ); 502 if( c.isCollapsed() || c.isHidden() ) 503 continue; 504 int currentSize = c.getSize( c.getParent(), dividerSize ); 505 int minSize = c.getMinSize( c.getParent(), dividerSize ); 506 if( currentSize - minSize > 0 ) { 507 res += currentSize - minSize; 508 } else { 509 nonShrinkable.add( c ); 510 } 511 } 512 513 cells.removeAll( nonShrinkable ); 514 for( int i=0; i<cells.size(); i++ ) { 515 GridSplitCell c = (GridSplitCell)cells.get( i ); 516 int currentSize = c.getSize( c.getParent(), dividerSize ); 517 int minSize = c.getMinSize( c.getParent(), dividerSize ); 518 c.normalizedResizeWeight = 1.0*(currentSize-minSize)/res; 519 } 520 return res; 521 } 522 523 526 private void distributeDelta( int delta, ArrayList cells, int dividerSize ) { 527 int totalDistributed = 0; 528 for( int i=0; i<cells.size(); i++ ) { 529 GridSplitCell child = (GridSplitCell)cells.get( i ); 530 int childDelta = (int)(child.normalizedResizeWeight*delta); 531 totalDistributed += childDelta; 532 if( i == cells.size()-1 ) childDelta += delta - totalDistributed; 534 child.setSize( this, child.getSize( this, dividerSize ) + childDelta ); 535 } 536 } 537 538 541 private void normalizeResizeWeights( List cells ) { 542 if( cells.isEmpty() ) 543 return; 544 545 double totalWeight = 0.0; 546 for( Iterator i=cells.iterator(); i.hasNext(); ) { 547 GridSplitCell c = (GridSplitCell)i.next(); 548 totalWeight += c.resizeWeight; 549 } 550 551 double deltaWeight = (1.0 - totalWeight) / cells.size(); 552 553 for( Iterator i=cells.iterator(); i.hasNext(); ) { 554 GridSplitCell c = (GridSplitCell)i.next(); 555 c.normalizedResizeWeight = c.resizeWeight + deltaWeight; 556 } 557 } 558 559 562 public void setCollapsed( boolean collapsed ) { 563 if( this.collapsed == collapsed ) { 564 return; 565 } 566 this.collapsed = collapsed; 567 if( isSplit() ) { 568 for( Iterator i=children.iterator(); i.hasNext(); ) { 569 GridSplitCell cell = (GridSplitCell)i.next(); 570 cell.setCollapsed( collapsed ); 571 } 572 } else { 573 component.setVisible( !collapsed ); 574 } 575 } 576 577 public boolean isCollapsed() { 578 return collapsed; 579 } 580 581 public void setHidden( boolean hidden ) { 582 this.isHidden = hidden; 583 } 584 585 public boolean isHidden() { 586 return isHidden; 587 } 588 589 private int getSize( GridSplitCell splitCell, int dividerSize ) { 590 if( splitCell.isHorizontalSplitter() ) 591 return getDimension( dividerSize ).width; 592 return getDimension( dividerSize ).height; 593 } 594 595 private int getMinSize( GridSplitCell splitCell, int dividerSize ) { 596 if( splitCell.isHorizontalSplitter() ) 597 return getMinimumSize( dividerSize ).width; 598 return getMinimumSize( dividerSize ).height; 599 } 600 601 private void setSize( GridSplitCell splitCell, int newSize ) { 602 if( isCollapsed() || isHidden() ) 603 return; 604 605 if( splitCell.isHorizontalSplitter() ) 606 dimension.width = newSize; 607 else 608 dimension.height = newSize; 609 } 610 611 private void setDimension( Dimension newDimension ) { 612 if( isCollapsed() || isHidden() ) 613 return; 614 615 this.dimension = newDimension; 616 } 617 618 public Dimension getDimension( int dividerSize ) { 619 if( isHidden() || isCollapsed() ) 620 return new Dimension ( 0, 0 ); 621 622 if( isSplit() ) { 623 Dimension res = new Dimension ( 0, 0 ); 624 ArrayList visibleCells = getVisibleCells(); 625 for( int i=0; i<visibleCells.size(); i++ ) { 626 GridSplitCell child = (GridSplitCell)visibleCells.get( i ); 627 Dimension childDim = child.getDimension( dividerSize ); 628 if( isHorizontalSplitter() ) { 629 res.height = this.dimension.height; 630 res.width += childDim.width; 631 } else { 632 res.height += childDim.height; 633 res.width = this.dimension.width; 634 } 635 } 636 if( isHorizontalSplitter() ) { 637 res.width += (visibleCells.size()-1)*dividerSize; 638 } else { 639 res.height += (visibleCells.size()-1)*dividerSize; 640 } 641 return res; 642 } 643 return new Dimension ( dimension ); 644 } 645 646 private static void copy( GridSplitCell source, GridSplitCell target ) { 647 target.component = source.component; 648 target.children = new ArrayList ( source.children ); 649 for( Iterator i=target.children.iterator(); i.hasNext(); ) { 650 GridSplitCell cell = (GridSplitCell)i.next(); 651 cell.setParent( target ); 652 } 653 target.resizeWeight = source.resizeWeight; 655 target.splitterOrientation = source.splitterOrientation; 656 target.collapsed = source.collapsed; 657 target.dimension = new Dimension ( source.dimension ); 658 target.isHidden = source.isHidden; 659 target.location = new Point ( source.location ); 660 } 661 662 666 void setLocation( int x, int y, int dividerSize ) { 667 location.x = x; 668 location.y = y; 669 if( isSplit() ) { 670 ArrayList visibleCells = getVisibleCells(); 671 int childX = x; 672 int childY = y; 673 for( int i=0; i<visibleCells.size(); i++ ) { 674 GridSplitCell child = (GridSplitCell)visibleCells.get( i ); 675 Dimension d = child.getDimension( dividerSize ); 676 child.setLocation( childX, childY, dividerSize ); 677 678 if( isHorizontalSplitter() ) 679 childX += d.width + dividerSize; 680 else 681 childY += d.height + dividerSize; 682 } 683 } else { 684 component.setLocation( location ); 685 component.setSize( dimension ); 686 } 687 } 688 689 Point getLocation() { 690 return new Point ( location ); 691 } 692 } | Popular Tags |