KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > piagetproject > gridsplit > GridSplitCell


1 /*
2  * AbstractHierarchyElement.java
3  *
4  * Created on 19 November 2004, 16:26
5  */

6
7 package org.netbeans.modules.piagetproject.gridsplit;
8
9 import java.awt.Component JavaDoc;
10 import java.awt.Dimension JavaDoc;
11 import java.awt.Point JavaDoc;
12 import java.awt.Rectangle JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.List JavaDoc;
15 import java.util.ArrayList JavaDoc;
16
17 /**
18  *
19  * @author aubrecht
20  */

21 public class GridSplitCell {
22     
23     public static final int NORTH = 1;
24     public static final int SOUTH = 2;
25     public static final int WEST = 4;
26     public static final int EAST = 8;
27     
28     public static final int NO_SPLITTER = 0;
29     public static final int HORIZONTAL_SPLITTER = NORTH + SOUTH;
30     public static final int VERTICAL_SPLITTER = WEST + EAST;
31     
32     
33     //the parent cell in the hierarchy or null if this is a root cell
34
private GridSplitCell parent;
35     //children cell, empty if this is a leaf cell (i.e. cell with an actual swing component)
36
private List JavaDoc children = new ArrayList JavaDoc();
37     //split orientation, HORIZONTAL means that the split bars are vertical
38
private int splitterOrientation = NO_SPLITTER;
39     //swing component displayed in this cell or null if this cell has children cells
40
private Component JavaDoc component;
41     //defines the portion of the resizing delta consumed by this cell
42
private double resizeWeight = 0.0;
43     //normalized resize weight, used internally only
44
private double normalizedResizeWeight = 0.0;
45     //true if this cell is collapsed (has zero size), splitter is shown for this cell
46
private boolean collapsed = false;
47     //true if this component is hidden in the hierarchy, no splitter is visible for this cell
48
private boolean isHidden = false;
49     //cells current dimensions
50
private Dimension JavaDoc dimension = new Dimension JavaDoc(0,0);
51     //cells location in the SplitPane
52
private Point JavaDoc location = new Point JavaDoc(0,0);
53     
54     GridSplitCell( Component JavaDoc c, double resizeWeight ) {
55         this( c, resizeWeight, new Dimension JavaDoc(0,0) );
56     }
57
58     GridSplitCell( Component JavaDoc c, double resizeWeight, Dimension JavaDoc initialDimension ) {
59         this.component = c;
60         this.resizeWeight = resizeWeight;
61         this.dimension.width = initialDimension.width;
62         this.dimension.height = initialDimension.height;
63     }
64     
65     /**
66      * Copy constructor
67      */

68     GridSplitCell( GridSplitCell src ) {
69         copy( src, this );
70         this.parent = src.parent;
71     }
72     
73     public GridSplitCell getParent() {
74         return parent;
75     }
76     
77     void setParent( GridSplitCell newParent ) {
78         this.parent = newParent;
79     }
80     
81     /**
82      * @return Number of children cells or zero if this cell is not split.
83      */

84     public int count() {
85         return children.size();
86     }
87     
88     /**
89      * @return Number of children cells that are not hidden (i.e. a split bar is shown for them)
90      */

91     public int countVisibleCells() {
92         //TODO refactor this method
93
return getVisibleCells().size();
94     }
95     
96     /**
97      * @return Children cell at given position.
98      */

99     public GridSplitCell cellAt( int index ) {
100         return (GridSplitCell) children.get( index );
101     }
102     
103     /**
104      * @return True if this is a root cell (i.e. the top-most cell in the hierarchy).
105      */

106     public boolean isRootCell() {
107         return null == getParent();
108     }
109     
110     /**
111      * Add a new cell with given compent to this component's side. This cell may split itself if needed.
112      * @param compToAdd Component to be added to split hierarchy.
113      * @param side This cell's side that will be shared with the new component.
114      * @param initialSize The percentage of this cell's parent's size that will be used as new component's initial dimension.
115      * @param resizeWeight New component's resize weight.
116      * @return Split cell encapsulating the new component.
117      */

118     public GridSplitCell addToSide( Component JavaDoc compToAdd, int side, double initialSize, double resizeWeight ) {
119         GridSplitCell newChild = new GridSplitCell( compToAdd, resizeWeight );
120         GridSplitCell owner;
121         GridSplitCell neighbor;
122         if( isRootCell() ) {
123             //adding a component to the root cell, i.e. the component will be attached
124
//to split pane's side
125
owner = this;
126             if( isSplitterSide( side ) ) {
127                 //the root cell is already split and the new component will share
128
//a split side
129
if( side == NORTH || side == WEST ) {
130                     neighbor = cellAt( 0 );
131                 } else {
132                     neighbor = cellAt( count()-1 );
133                 }
134             } else {
135                 //the root is either not split yet or the share side does not match split orientation
136
owner = this;
137                 neighbor = split();
138             }
139         } else {
140             owner = getParent();
141             if( owner.isSplitterSide( side ) ) {
142                 //the cell is already split and the shared side matches existing split orientation
143
neighbor = this;
144             } else {
145                 //the cell is either not split yet or the share side does not match split orientation
146
owner = this;
147                 neighbor = split();
148             }
149         }
150         owner.addToSide( neighbor, newChild, side, initialSize );
151         return newChild;
152     }
153     
154     /**
155      * Split this cell. Its current contents is copied to a new child cell.
156      */

157     private GridSplitCell split() {
158         GridSplitCell child = new GridSplitCell( this );
159         
160         children.clear();
161         children.add( child );
162         child.setParent( this );
163         component = null;
164         
165         return child;
166     }
167     
168     /**
169      * Add a new child cell and set its initial dimensions.
170      */

171     private void addToSide( GridSplitCell neighbor, GridSplitCell newChild, int side, double initialSize ) {
172         Dimension JavaDoc parentSize = getDimension( 0 );
173         splitterOrientation = (side == NORTH || side == SOUTH) ? VERTICAL_SPLITTER : HORIZONTAL_SPLITTER;
174         int insertIndex = children.indexOf( neighbor );
175         assert insertIndex >= 0;
176         if( side == SOUTH || side == EAST )
177             insertIndex++;
178         if( insertIndex > children.size() )
179             children.add( newChild );
180         else
181             children.add( insertIndex, newChild );
182         if( isHorizontalSplitter() ) {
183             newChild.dimension = new Dimension JavaDoc( (int)(parentSize.width*initialSize), parentSize.height );
184         } else {
185             newChild.dimension = new Dimension JavaDoc( parentSize.width, (int)(parentSize.height*initialSize) );
186         }
187         newChild.setParent( this );
188     }
189     
190     /**
191      * @return True if given side matches current splitter orientation.
192      */

193     boolean isSplitterSide( int side ) {
194         return (isHorizontalSplitter() && (side == WEST || side == EAST))
195             || (isVerticalSplitter() && (side == SOUTH || side == NORTH));
196     }
197
198     /**
199      * Remove given cell from the list of children cells. The hierarchy is rearranged
200      * if this cell has only one child left after the removal.
201      */

202     public void remove( GridSplitCell child ) {
203         assert children.contains( child );
204         
205         children.remove( child );
206         if( children.size() == 1 ) {
207             GridSplitCell orphan = cellAt( 0 );
208             
209             if( null != getParent() ) {
210                 //replace this cell with orphan's contents in the parent cell
211
getParent().replace( this, orphan );
212             } else {
213                 //this is a root cell, so just take over all properties from the orphan
214
copy( orphan, this );
215             }
216         }
217     }
218     
219     /**
220      * Replace the origCell with newCell.
221      */

222     private void replace( GridSplitCell origCell, GridSplitCell newCell ) {
223         assert children.contains( origCell );
224         int index = children.indexOf( origCell );
225         if( newCell.isSplit() && newCell.splitterOrientation == splitterOrientation ) {
226             children.addAll( index, newCell.children );
227             for( Iterator JavaDoc i=newCell.children.iterator(); i.hasNext(); ) {
228                 GridSplitCell c = (GridSplitCell)i.next();
229                 c.setParent( this );
230             }
231             children.remove( origCell );
232         } else {
233             children.set( index, newCell );
234             newCell.setParent( this );
235         }
236     }
237     
238     /**
239      * @return True if this cell is a splitter cell.
240      */

241     public boolean isSplit() {
242         return null == component || children.size() == 1;
243     }
244     
245     public int getSplitOrientation() {
246         return splitterOrientation;
247     }
248     
249     public boolean isHorizontalSplitter() {
250         return splitterOrientation == HORIZONTAL_SPLITTER;
251     }
252     
253     public boolean isVerticalSplitter() {
254         return splitterOrientation == VERTICAL_SPLITTER;
255     }
256
257     public Component JavaDoc getComponent() {
258         return component;
259     }
260
261     /**
262      * @param dividerSize The width of splitter bar.
263      * @return The minimum size of this cell. If this cell is a split cell then the
264      * result is a sum of minimum sizes of all children cells.
265      */

266     public Dimension JavaDoc getMinimumSize( int dividerSize ) {
267         if( isCollapsed() || isHidden() ) {
268             return new Dimension JavaDoc( 0, 0 );
269         } else {
270             if( isSplit() ) {
271                 return calculateMinimumSize( dividerSize );
272             } else {
273                 return component.getMinimumSize();
274             }
275         }
276     }
277     
278     /**
279      * @return Sum of minimum sizes of all children cells.
280      */

281     private Dimension JavaDoc calculateMinimumSize( int dividerSize ) {
282         Dimension JavaDoc minSize = new Dimension JavaDoc( 0, 0 );
283         ArrayList JavaDoc visibleCells = getVisibleCells();
284         for( Iterator JavaDoc i=visibleCells.iterator(); i.hasNext(); ) {
285             GridSplitCell child = (GridSplitCell)i.next();
286             
287             Dimension JavaDoc childMinSize = child.getMinimumSize( dividerSize );
288             
289             if( isHorizontalSplitter() ) {
290                 minSize.width += childMinSize.width;
291                 if( childMinSize.height > minSize.height )
292                     minSize.height = childMinSize.height;
293             } else {
294                 minSize.height += childMinSize.height;
295                 if( childMinSize.width > minSize.width )
296                     minSize.width = childMinSize.width;
297             }
298         }
299         if( isHorizontalSplitter() ) {
300             minSize.width += (visibleCells.size()-1)*dividerSize;
301         } else {
302             minSize.height += (visibleCells.size()-1)*dividerSize;
303         }
304         
305         return minSize;
306     }
307
308     /**
309      * @return List of children cells that are not hidden.
310      */

311     ArrayList JavaDoc getVisibleCells() {
312         ArrayList JavaDoc res = new ArrayList JavaDoc( children.size() );
313         for( Iterator JavaDoc i=children.iterator(); i.hasNext(); ) {
314             GridSplitCell child = (GridSplitCell)i.next();
315             if( child.isHidden() )
316                 continue;
317             res.add( child );
318         }
319         return res;
320     }
321     
322     /**
323      * @return List of children cells that can be resized (not hidden and not collapsed).
324      */

325     ArrayList JavaDoc getResizeableCells() {
326         ArrayList JavaDoc res = new ArrayList JavaDoc( children.size() );
327         for( Iterator JavaDoc i=children.iterator(); i.hasNext(); ) {
328             GridSplitCell child = (GridSplitCell)i.next();
329             if( child.isHidden() || child.isCollapsed() )
330                 continue;
331             res.add( child );
332         }
333         return res;
334     }
335     
336     /**
337      * @return List of children cells with non-zero resize weight.
338      */

339     ArrayList JavaDoc getResizeHungryCells() {
340         ArrayList JavaDoc res = new ArrayList JavaDoc( children.size() );
341         for( Iterator JavaDoc i=children.iterator(); i.hasNext(); ) {
342             GridSplitCell child = (GridSplitCell)i.next();
343             if( child.isHidden() || child.isCollapsed() || child.resizeWeight == 0.0 )
344                 continue;
345             res.add( child );
346         }
347         return res;
348     }
349     
350     /**
351      * Adjust cell's dimensions.
352      */

353     void resize( int width, int height, int dividerSize ) {
354         if( isSplit() ) {
355             
356             //find out what the delta is
357
int currentSize = getSize( this, dividerSize );
358             int newNetSize = (isHorizontalSplitter() ? width : height);
359             int delta = newNetSize - currentSize;
360             //System.out.println( "Delta is: " + delta );
361

362             if( delta > 0 ) {
363                 //the child cells will grow
364

365                 grow( delta, dividerSize );
366                 
367             } else if( delta < 0 ) {
368                 
369                 delta = shrink( delta, dividerSize );
370
371                 if( delta > 0 ) {
372                     //the complete delta couldn't be distributed because of minimum sizes
373
//System.out.println( "Remaining size not distributed: " + delta );
374
newNetSize -= delta;
375                     if( isHorizontalSplitter() )
376                         width -= delta;
377                     else
378                         height -= delta;
379                 }
380             }
381             
382             //resize all child cells
383
int totalSize = 0;
384             ArrayList JavaDoc visibleCells = getVisibleCells();
385             for( int i=0; i<visibleCells.size(); i++ ) {
386                 GridSplitCell child = (GridSplitCell)visibleCells.get( i );
387                 int childWidth = isHorizontalSplitter() ? child.getSize( this, dividerSize ) : width;
388                 int childHeight = isHorizontalSplitter() ? height : child.getSize( this, dividerSize );
389                 
390                 if( isHorizontalSplitter() ) {
391                     totalSize += childWidth;
392                     if( i == visibleCells.size()-1 && totalSize < newNetSize ) {
393                         //XXX this is probably overkill, this adjustment is already done in distributeDelta()
394
//adjust rounding errors (+/- one pixel) for the last cell
395
System.out.println( "Extra width: " + (newNetSize - totalSize));
396                         childWidth += newNetSize - totalSize;
397                         //child.dimension.width += newNetSize - totalSize;
398
} else {
399                         totalSize += dividerSize;
400                     }
401                 } else {
402                     totalSize += childHeight;
403                     if( i == visibleCells.size()-1 && totalSize < newNetSize ) {
404                         //adjust rounding errors (+/- one pixel) for the last cell
405
//XXX this is probably overkill, this adjustment is already done in distributeDelta()
406
System.out.println( "Extra height: " + (newNetSize - totalSize));
407                         childHeight += newNetSize - totalSize;
408                         //child.dimension.height += newNetSize - totalSize;
409
} else {
410                         totalSize += dividerSize;
411                     }
412                 }
413                 child.resize( childWidth, childHeight, dividerSize );
414             }
415             if( !(isCollapsed() || isHidden()) ) {
416                 if( isHorizontalSplitter() )
417                     setDimension( new Dimension JavaDoc( newNetSize, height ) );
418                 else
419                     setDimension( new Dimension JavaDoc( width, newNetSize ) );
420             }
421         } else {
422             setDimension( new Dimension JavaDoc( width, height ) );
423         }
424     }
425     
426     /**
427      * Grow children cell dimensions.
428      */

429     private void grow( int delta, int dividerSize ) {
430         //children with resize weight > 0 that are not collapsed
431
ArrayList JavaDoc hungryCells = getResizeHungryCells();
432
433         //grow some/all child windows
434
if( !hungryCells.isEmpty() ) {
435             //we have children with non-zero resize weight so let them consume the whole delta
436
normalizeResizeWeights( hungryCells );
437             distributeDelta( delta, hungryCells, dividerSize );
438         } else {
439             //resize all children proportionally
440
ArrayList JavaDoc resizeableCells = getResizeableCells();
441             normalizeResizeWeights( resizeableCells );
442             distributeDelta( delta, resizeableCells, dividerSize );
443         }
444     }
445     
446     /**
447      * Shrink children cell dimensions.
448      * The children cells will not shrink below their minimum sizes.
449      *
450      * @return The remaining resize delta that has not been distributed among children cells.
451      */

452     private int shrink( int negativeDelta, int dividerSize ) {
453         int delta = -negativeDelta;
454
455         //children with resize weight > 0 that are not collapsed
456
ArrayList JavaDoc hungryCells = getResizeHungryCells();
457
458         //first find out how much cells with non-zero resize weight can shrink
459
int resizeArea = calculateShrinkableArea( hungryCells, dividerSize );
460         if( resizeArea >= delta ) {
461             resizeArea = delta;
462             delta = 0;
463         } else {
464             delta -= resizeArea;
465         }
466         if( resizeArea > 0 ) {
467             //shrink cells with non-zero resize weight
468
distributeDelta( -resizeArea, hungryCells, dividerSize );
469         }
470
471         if( delta > 0 ) {
472             //hungry cells did not consume the complete delta,
473
//distribute the remaining delta among other resizeable cells
474
ArrayList JavaDoc resizeableCells = getResizeableCells();
475
476             resizeArea = calculateShrinkableArea( resizeableCells, dividerSize );
477             if( resizeArea >= delta ) {
478                 resizeArea = delta;
479                 delta = 0;
480             } else {
481                 delta -= resizeArea;
482             }
483             if( resizeArea > 0 ) {
484                 distributeDelta( -resizeArea, resizeableCells, dividerSize );
485             }
486         }
487         return delta;
488     }
489     
490     /**
491      * Sum up the available resize space of given cells. The resize space is the difference
492      * between child cell's current size and child cell's minimum size.
493      * Children cells that cannot be resized are removed from the given list and
494      * resize weights of remaining cells are normalized.
495      */

496     private int calculateShrinkableArea( ArrayList JavaDoc cells, int dividerSize ) {
497         int res = 0;
498         ArrayList JavaDoc nonShrinkable = new ArrayList JavaDoc( cells.size() );
499         for( int i=0; i<cells.size(); i++ ) {
500             GridSplitCell c = (GridSplitCell)cells.get( i );
501             if( c.isCollapsed() || c.isHidden() )
502                 continue;
503             int currentSize = c.getSize( c.getParent(), dividerSize );
504             int minSize = c.getMinSize( c.getParent(), dividerSize );
505             if( currentSize - minSize > 0 ) {
506                 res += currentSize - minSize;
507             } else {
508                 nonShrinkable.add( c );
509             }
510         }
511         
512         cells.removeAll( nonShrinkable );
513         for( int i=0; i<cells.size(); i++ ) {
514             GridSplitCell c = (GridSplitCell)cells.get( i );
515             int currentSize = c.getSize( c.getParent(), dividerSize );
516             int minSize = c.getMinSize( c.getParent(), dividerSize );
517             c.normalizedResizeWeight = 1.0*(currentSize-minSize)/res;
518         }
519         return res;
520     }
521     
522     /**
523      * Distribute the given delta among given cell dimensions using their normalized weights.
524      */

525     private void distributeDelta( int delta, ArrayList JavaDoc cells, int dividerSize ) {
526         int totalDistributed = 0;
527         for( int i=0; i<cells.size(); i++ ) {
528             GridSplitCell child = (GridSplitCell)cells.get( i );
529             int childDelta = (int)(child.normalizedResizeWeight*delta);
530             totalDistributed += childDelta;
531             if( i == cells.size()-1 ) //fix rounding errors
532
childDelta += delta - totalDistributed;
533             child.setSize( this, child.getSize( this, dividerSize ) + childDelta );
534         }
535     }
536     
537     /**
538      * Normalize resize weights so that their sum equals to 1.
539      */

540     private void normalizeResizeWeights( List JavaDoc cells ) {
541         if( cells.isEmpty() )
542             return;
543         
544         double totalWeight = 0.0;
545         for( Iterator JavaDoc i=cells.iterator(); i.hasNext(); ) {
546             GridSplitCell c = (GridSplitCell)i.next();
547             totalWeight += c.resizeWeight;
548         }
549         
550         double deltaWeight = (1.0 - totalWeight) / cells.size();
551
552         for( Iterator JavaDoc i=cells.iterator(); i.hasNext(); ) {
553             GridSplitCell c = (GridSplitCell)i.next();
554             c.normalizedResizeWeight = c.resizeWeight + deltaWeight;
555         }
556     }
557     
558     /**
559      * Set the collapsed flag for this cell and all its children.
560      */

561     public void setCollapsed( boolean collapsed ) {
562         if( this.collapsed == collapsed ) {
563             return;
564         }
565         this.collapsed = collapsed;
566         if( isSplit() ) {
567             for( Iterator JavaDoc i=children.iterator(); i.hasNext(); ) {
568                 GridSplitCell cell = (GridSplitCell)i.next();
569                 cell.setCollapsed( collapsed );
570             }
571         } else {
572             component.setVisible( !collapsed );
573         }
574     }
575     
576     public boolean isCollapsed() {
577         return collapsed;
578     }
579     
580     public void setHidden( boolean hidden ) {
581         this.isHidden = hidden;
582     }
583     
584     public boolean isHidden() {
585         return isHidden;
586     }
587     
588     private int getSize( GridSplitCell splitCell, int dividerSize ) {
589         if( splitCell.isHorizontalSplitter() )
590             return getDimension( dividerSize ).width;
591         return getDimension( dividerSize ).height;
592     }
593     
594     private int getMinSize( GridSplitCell splitCell, int dividerSize ) {
595         if( splitCell.isHorizontalSplitter() )
596             return getMinimumSize( dividerSize ).width;
597         return getMinimumSize( dividerSize ).height;
598     }
599     
600     private void setSize( GridSplitCell splitCell, int newSize ) {
601         if( isCollapsed() || isHidden() )
602             return;
603         
604         if( splitCell.isHorizontalSplitter() )
605             dimension.width = newSize;
606         else
607             dimension.height = newSize;
608     }
609     
610     private void setDimension( Dimension JavaDoc newDimension ) {
611         if( isCollapsed() || isHidden() )
612             return;
613         
614         this.dimension = newDimension;
615     }
616     
617     public Dimension JavaDoc getDimension( int dividerSize ) {
618         if( isHidden() || isCollapsed() )
619             return new Dimension JavaDoc( 0, 0 );
620         
621         if( isSplit() ) {
622             Dimension JavaDoc res = new Dimension JavaDoc( 0, 0 );
623             ArrayList JavaDoc visibleCells = getVisibleCells();
624             for( int i=0; i<visibleCells.size(); i++ ) {
625                 GridSplitCell child = (GridSplitCell)visibleCells.get( i );
626                 Dimension JavaDoc childDim = child.getDimension( dividerSize );
627                 if( isHorizontalSplitter() ) {
628                     res.height = this.dimension.height;
629                     res.width += childDim.width;
630                 } else {
631                     res.height += childDim.height;
632                     res.width = this.dimension.width;
633                 }
634             }
635             if( isHorizontalSplitter() ) {
636                 res.width += (visibleCells.size()-1)*dividerSize;
637             } else {
638                 res.height += (visibleCells.size()-1)*dividerSize;
639             }
640             return res;
641         }
642         return new Dimension JavaDoc( dimension );
643     }
644     
645     private static void copy( GridSplitCell source, GridSplitCell target ) {
646         target.component = source.component;
647         target.children = new ArrayList JavaDoc( source.children );
648         for( Iterator JavaDoc i=target.children.iterator(); i.hasNext(); ) {
649             GridSplitCell cell = (GridSplitCell)i.next();
650             cell.setParent( target );
651         }
652         //target.dividerSize = source.dividerSize;
653
target.resizeWeight = source.resizeWeight;
654         target.splitterOrientation = source.splitterOrientation;
655         target.collapsed = source.collapsed;
656         target.dimension = new Dimension JavaDoc( source.dimension );
657         target.isHidden = source.isHidden;
658         target.location = new Point JavaDoc( source.location );
659     }
660     
661     /**
662      * Set cell's new location. If the cell contains an actual swing component
663      * then its bounds and location are also set.
664      */

665     void setLocation( int x, int y, int dividerSize ) {
666         location.x = x;
667         location.y = y;
668         if( isSplit() ) {
669             ArrayList JavaDoc visibleCells = getVisibleCells();
670             int childX = x;
671             int childY = y;
672             for( int i=0; i<visibleCells.size(); i++ ) {
673                 GridSplitCell child = (GridSplitCell)visibleCells.get( i );
674                 Dimension JavaDoc d = child.getDimension( dividerSize );
675                 child.setLocation( childX, childY, dividerSize );
676
677                 if( isHorizontalSplitter() )
678                     childX += d.width + dividerSize;
679                 else
680                     childY += d.height + dividerSize;
681             }
682         } else {
683             component.setLocation( location );
684             component.setSize( dimension );
685         }
686     }
687     
688     Point JavaDoc getLocation() {
689         return new Point JavaDoc( location );
690     }
691 }
Popular Tags