1 19 20 package org.netbeans.core.windows.view.ui; 21 22 import java.awt.Component ; 23 import java.awt.Container ; 24 import java.awt.Cursor ; 25 import java.awt.Dimension ; 26 import java.awt.Graphics ; 27 import java.awt.LayoutManager ; 28 import java.awt.Point ; 29 import java.awt.event.MouseEvent ; 30 import java.awt.event.MouseListener ; 31 import java.awt.event.MouseMotionListener ; 32 import java.util.ArrayList ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import javax.accessibility.Accessible ; 36 import javax.accessibility.AccessibleContext ; 37 import javax.accessibility.AccessibleRole ; 38 import javax.accessibility.AccessibleState ; 39 import javax.accessibility.AccessibleStateSet ; 40 import javax.swing.JPanel ; 41 import javax.swing.JSplitPane ; 42 import javax.swing.UIManager ; 43 import org.netbeans.core.windows.view.ViewElement; 44 45 46 47 48 55 public class MultiSplitPane extends JPanel 56 implements MouseMotionListener , MouseListener { 57 58 private MultiSplitDivider draggingDivider; 60 private ArrayList <MultiSplitDivider> dividers = new ArrayList <MultiSplitDivider>(); 62 private boolean dirty = true; 64 private ArrayList <MultiSplitCell> cells = new ArrayList <MultiSplitCell>(); 66 private int orientation; 68 private int dividerSize; 70 71 private boolean userMovedSplit = false; 72 73 public MultiSplitPane() { 74 setLayout( new MultiSplitLayout() ); 75 addMouseMotionListener( this ); 76 addMouseListener( this ); 77 78 dividerSize = UIManager.getInt("SplitPane.dividerSize"); if( 0 == dividerSize ) 81 dividerSize = 7; 82 } 83 84 95 public void setChildren( int orientation, 96 ViewElement[] childrenViews, 97 double[] splitWeights ) { 98 99 assert childrenViews.length == splitWeights.length; 100 101 this.orientation = orientation; 102 103 List <Component > currentComponents = collectComponents(); 105 106 cells.clear(); 107 for( int i=0; i<childrenViews.length; i++ ) { 108 cells.add( new MultiSplitCell( childrenViews[i], 109 splitWeights[i], 110 isHorizontalSplit() ) ); 111 } 112 List <Component > updatedComponents = collectComponents(); 113 114 ArrayList <Component > removed = new ArrayList <Component >( currentComponents ); 115 removed.removeAll( updatedComponents ); ArrayList <Component > added = new ArrayList <Component >( updatedComponents ); 117 added.removeAll( currentComponents ); 119 for(Component c: removed) { 120 remove( c ); 121 } 122 123 for(Component c: added) { 124 add( c ); 125 } 126 127 dirty = true; 128 } 129 130 int getCellCount() { 131 return cells.size(); 132 } 133 134 MultiSplitCell cellAt( int index ) { 135 assert index >= 0; 136 assert index < cells.size(); 137 return (MultiSplitCell)cells.get( index ); 138 } 139 140 143 public void removeViewElementAt( int index ) { 144 if( index < 0 || index >= cells.size() ) 145 return; 146 MultiSplitCell cellToRemove = (MultiSplitCell)cells.remove( index ); 147 remove( cellToRemove.getComponent() ); 148 dirty = true; 149 } 150 151 public int getOrientation() { 152 return orientation; 153 } 154 155 public boolean isVerticalSplit() { 156 return orientation == JSplitPane.VERTICAL_SPLIT; 157 } 158 159 public boolean isHorizontalSplit() { 160 return orientation == JSplitPane.HORIZONTAL_SPLIT; 161 } 162 163 private List <Component > collectComponents() { 164 ArrayList <Component > res = new ArrayList <Component >( getCellCount() ); 165 for( int i=0; i<getCellCount(); i++ ) { 166 MultiSplitCell cell = cellAt( i ); 167 Component c = cell.getComponent(); 168 assert null != c; 169 res.add( c ); 170 } 171 return res; 172 } 173 174 177 public void calculateSplitWeights( List <ViewElement> visibleViews, List <Double > splitWeights ) { 178 double size = isHorizontalSplit() ? getSize().width : getSize().height; 179 if( size <= 0.0 ) 180 return; 181 for( int i=0; i<getCellCount(); i++ ) { 182 MultiSplitCell cell = cellAt( i ); 183 double weight = cell.getSize() / size; 184 splitWeights.add( Double.valueOf( weight ) ); 185 visibleViews.add( cell.getViewElement() ); 186 } 187 } 188 189 public int getDividerSize() { 190 return dividerSize; 191 } 192 193 public void setDividerSize( int newDividerSize ) { 194 dirty |= newDividerSize != dividerSize; 195 this.dividerSize = newDividerSize; 196 } 197 198 public Dimension getMinimumSize() { 199 Dimension d = new Dimension (); 201 for( int i=0; i<getCellCount(); i++ ) { 202 MultiSplitCell cell = cellAt( i ); 203 int size = cell.getMinimumSize(); 204 Dimension minDim = cell.getComponent().getMinimumSize(); 205 if( isHorizontalSplit() ) { 206 d.width += size; 207 if( minDim.height > d.height ) 208 d.height = minDim.height; 209 } else { 210 d.height += size; 211 if( minDim.width > d.width ) 212 d.width = minDim.width; 213 } 214 } 215 if( isHorizontalSplit() ) { 217 d.width += (getCellCount()-1) * getDividerSize(); 218 } else { 219 d.height += (getCellCount()-1) * getDividerSize(); 220 } 221 return d; 222 } 223 224 public void mouseMoved( MouseEvent e ) { 225 switchCursor( e ); 226 e.consume(); 227 } 228 229 public void mouseDragged( MouseEvent e ) { 230 if( null == draggingDivider ) 231 return; 232 233 draggingDivider.dragTo( e.getPoint() ); 234 e.consume(); 235 } 236 237 public void mouseReleased(MouseEvent e) { 238 if( null == draggingDivider ) 239 return; 240 241 final Point p = new Point ( e.getPoint() ); 242 draggingDivider.finishDraggingTo( p ); 243 draggingDivider = null; 244 setCursor( Cursor.getDefaultCursor() ); 245 e.consume(); 246 } 247 248 public void mousePressed(MouseEvent e) { 249 MultiSplitDivider divider = dividerAtPoint( e.getPoint() ); 250 if( null == divider ) 251 return; 252 253 draggingDivider = divider; 254 divider.startDragging( e.getPoint() ); 255 e.consume(); 256 } 257 258 public void mouseExited(MouseEvent e) { 259 if( null == draggingDivider ) { 260 setCursor( Cursor.getDefaultCursor() ); 261 } 262 e.consume(); 263 } 264 265 public void mouseEntered(MouseEvent e) { 266 } 267 268 public void mouseClicked(MouseEvent e) { 269 } 270 271 272 private void switchCursor( MouseEvent e ) { 273 MultiSplitDivider divider = dividerAtPoint( e.getPoint() ); 274 if( null == divider ) { 275 setCursor( Cursor.getDefaultCursor() ); 276 } else { 277 if( divider.isHorizontal() ) { 278 setCursor( Cursor.getPredefinedCursor( Cursor.E_RESIZE_CURSOR ) ); 279 } else { 280 setCursor( Cursor.getPredefinedCursor( Cursor.N_RESIZE_CURSOR ) ); 281 } 282 } 283 } 284 285 private MultiSplitDivider dividerAtPoint( Point p ) { 286 for(MultiSplitDivider d: dividers) { 287 if( d.containsPoint( p ) ) 288 return d; 289 } 290 return null; 291 } 292 293 public void paint( Graphics g ) { 294 super.paint(g); 295 for(MultiSplitDivider divider: dividers) { 297 divider.paint( g ); 298 } 299 } 300 301 306 private void resize( int newSize ) { 307 int currentSize = 0; 309 for( int i=0; i<getCellCount(); i++ ) { 310 currentSize += cellAt( i ).getRequiredSize(); 311 } 312 int totalDividerSize = getDividerSize() * (getCellCount()-1); 313 int newNetSize = newSize - totalDividerSize; 314 int delta = newNetSize - currentSize; 315 316 if( delta > 0 ) { 317 319 grow( delta ); 320 321 } else if( delta < 0 ) { 322 323 delta = shrink( delta ); 324 325 if( delta > 0 ) { 326 newNetSize -= delta; 328 } 329 } 330 331 int totalSize = 0; 333 for( int i=0; i<getCellCount(); i++ ) { 334 MultiSplitCell cell = cellAt( i ); 335 totalSize += cell.getRequiredSize(); 336 } 337 if( totalSize < newNetSize ) { 338 MultiSplitCell lastCell = cellAt( getCellCount()-1 ); 339 lastCell.setRequiredSize( lastCell.getRequiredSize() + (newNetSize-totalSize) ); 340 } 341 } 342 343 346 private void grow( int delta ) { 347 List <MultiSplitCell> hungryCells = getResizeHungryCells(); 349 350 if( !hungryCells.isEmpty() ) { 352 normalizeResizeWeights( hungryCells ); 354 distributeDelta( delta, hungryCells ); 355 } else { 356 ArrayList <MultiSplitCell> resizeableCells = new ArrayList <MultiSplitCell>( cells ); 358 normalizeResizeWeights( resizeableCells ); 359 distributeDelta( delta, resizeableCells ); 360 } 361 } 362 363 369 private int shrink( int negativeDelta ) { 370 int delta = -negativeDelta; 371 372 List <MultiSplitCell> hungryCells = getResizeHungryCells(); 374 375 int resizeArea = calculateShrinkableArea( hungryCells ); 377 if( resizeArea >= delta ) { 378 resizeArea = delta; 379 delta = 0; 380 } else { 381 delta -= resizeArea; 382 } 383 if( resizeArea > 0 ) { 384 distributeDelta( -resizeArea, hungryCells ); 386 } 387 388 if( delta > 0 ) { 389 ArrayList <MultiSplitCell> resizeableCells = new ArrayList <MultiSplitCell>( cells ); 392 393 resizeArea = calculateShrinkableArea( resizeableCells ); 394 if( resizeArea >= delta ) { 395 resizeArea = delta; 396 delta = 0; 397 } else { 398 delta -= resizeArea; 399 } 400 if( resizeArea > 0 ) { 401 distributeDelta( -resizeArea, resizeableCells ); 402 } 403 } 404 return delta; 405 } 406 407 413 private int calculateShrinkableArea( List <MultiSplitCell> cells ) { 414 int res = 0; 415 ArrayList <MultiSplitCell> nonShrinkable = new ArrayList <MultiSplitCell>( cells.size() ); 416 for( int i=0; i<cells.size(); i++ ) { 417 MultiSplitCell c = (MultiSplitCell)cells.get( i ); 418 int currentSize = c.getRequiredSize(); 419 int minSize = c.getMinimumSize(); 420 if( currentSize - minSize > 0 ) { 421 res += currentSize - minSize; 422 } else { 423 nonShrinkable.add( c ); 424 } 425 } 426 427 cells.removeAll( nonShrinkable ); 428 for(MultiSplitCell c: cells) { 429 int currentSize = c.getRequiredSize(); 430 int minSize = c.getMinimumSize(); 431 c.setNormalizedResizeWeight( 1.0*(currentSize-minSize)/res ); 432 } 433 434 return res; 435 } 436 437 440 private void distributeDelta( int delta, List <MultiSplitCell> cells ) { 441 int totalDistributed = 0; 442 for( int i=0; i<cells.size(); i++ ) { 443 MultiSplitCell cell = cells.get( i ); 444 int cellDelta = (int)(cell.getNormalizedResizeWeight()*delta); 445 totalDistributed += cellDelta; 446 if( i == cells.size()-1 ) cellDelta += delta - totalDistributed; 448 cell.setRequiredSize( cell.getRequiredSize() + cellDelta ); 449 } 450 } 451 452 455 private void normalizeResizeWeights( List cells ) { 456 if( cells.isEmpty() ) 457 return; 458 459 double totalWeight = 0.0; 460 for( Iterator i=cells.iterator(); i.hasNext(); ) { 461 MultiSplitCell c = (MultiSplitCell)i.next(); 462 totalWeight += c.getResizeWeight(); 463 } 464 465 double deltaWeight = (1.0 - totalWeight) / cells.size(); 466 467 for( Iterator i=cells.iterator(); i.hasNext(); ) { 468 MultiSplitCell c = (MultiSplitCell)i.next(); 469 c.setNormalizedResizeWeight( c.getResizeWeight() + deltaWeight ); 470 } 471 } 472 473 476 List <MultiSplitCell> getResizeHungryCells() { 477 List <MultiSplitCell> res = new ArrayList <MultiSplitCell>( cells.size() ); 478 for( int i=0; i<getCellCount(); i++ ) { 479 MultiSplitCell cell = cellAt( i ); 480 if( cell.getResizeWeight() <= 0.0 ) 481 continue; 482 res.add( cell ); 483 } 484 return res; 485 } 486 487 490 void createDividers() { 491 dividers.clear(); 492 for( int i=0; i<getCellCount()-1; i++ ) { 493 MultiSplitCell first = cellAt( i ); 494 MultiSplitCell second = cellAt( i+1 ); 495 496 MultiSplitDivider divider = new MultiSplitDivider( MultiSplitPane.this, first, second ); 497 dividers.add( divider ); 498 } 499 } 500 501 void splitterMoved() { 502 userMovedSplit = true; 503 validate(); 504 } 505 506 509 public AccessibleContext getAccessibleContext() { 510 if( accessibleContext == null ) { 511 accessibleContext = new AccessibleMultiSplitPane(); 512 } 513 return accessibleContext; 514 } 515 516 int getDividerAccessibleIndex( MultiSplitDivider divider ) { 517 int res = dividers.indexOf( divider ); 518 res += getAccessibleContext().getAccessibleChildrenCount() - dividers.size(); 519 return res; 520 } 521 522 protected class AccessibleMultiSplitPane extends AccessibleJComponent { 523 public AccessibleStateSet getAccessibleStateSet() { 524 AccessibleStateSet states = super.getAccessibleStateSet(); 525 if( isHorizontalSplit() ) { 526 states.add( AccessibleState.HORIZONTAL ); 527 } else { 528 states.add( AccessibleState.VERTICAL ); 529 } 530 return states; 531 } 532 533 public AccessibleRole getAccessibleRole() { 534 return AccessibleRole.SPLIT_PANE; 535 } 536 537 public Accessible getAccessibleAt( Point p ) { 538 MultiSplitDivider divider = dividerAtPoint( p ); 539 if( null != divider ) { 540 return divider; 541 } 542 return super.getAccessibleAt( p ); 543 } 544 545 public Accessible getAccessibleChild(int i) { 546 547 int childrenCount = super.getAccessibleChildrenCount(); 548 if( i < childrenCount ) { 549 return super.getAccessibleChild( i ); 550 } 551 if( i-childrenCount >= dividers.size() ) { 552 return null; 553 } 554 555 MultiSplitDivider divider = dividers.get( i-childrenCount ); 556 return divider; 557 } 558 559 public int getAccessibleChildrenCount() { 560 return super.getAccessibleChildrenCount() + dividers.size(); 561 } 562 563 } 565 567 protected class MultiSplitLayout implements LayoutManager { 568 569 public void layoutContainer( Container c ) { 570 if( c != MultiSplitPane.this ) 571 return; 572 573 int newSize = isHorizontalSplit() ? getSize().width : getSize().height; 574 for( int i=0; i<getCellCount(); i++ ) { 577 MultiSplitCell cell = cellAt( i ); 578 cell.maybeResetToInitialSize( newSize ); 579 } 580 581 resize( newSize ); 583 584 layoutCells(); 586 587 if( userMovedSplit ) { 588 userMovedSplit = false; 590 firePropertyChange( "splitPositions", null, this ); 591 } 592 593 createDividers(); 595 } 596 597 private void layoutCells() { 598 int x = 0; 599 int y = 0; 600 int width = getWidth(); 601 int height = getHeight(); 602 for( int i=0; i<getCellCount(); i++ ) { 603 MultiSplitCell cell = cellAt( i ); 604 605 if( cell.getComponent().getParent() != MultiSplitPane.this ) { 608 add( cell.getComponent() ); 609 } 610 611 if( isHorizontalSplit() ) { 612 width = cell.getRequiredSize(); 613 } else { 614 height = cell.getRequiredSize(); 615 } 616 cell.layout( x, y, width, height ); 617 618 if( isHorizontalSplit() ) { 619 x += width; 620 if( i < getCellCount() ) { 621 x += getDividerSize(); 622 } 623 } else { 624 y += height; 625 if( i < getCellCount()-1 ) { 626 y += getDividerSize(); 627 } 628 } 629 } 630 } 631 632 public Dimension minimumLayoutSize(Container container) { 633 return container.getSize(); 634 } 635 636 public Dimension preferredLayoutSize(Container container) { 637 return container.getSize(); 638 } 639 640 public void removeLayoutComponent(Component c) {} 641 642 public void addLayoutComponent(String string, Component c) {} 643 } 644 } 645 | Popular Tags |