blob: 5845dcf72d9754ad86704ea0b5d7e3260acf9cd7 [file] [log] [blame]
// Created on 16.08.2009
package org.eclipse.swt.custom;
import java.util.*;
import org.eclipse.rwt.graphics.Graphics;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SpreadSheetModel.Adapter;
import org.eclipse.swt.custom.SpreadSheetModel.Event;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.*;
class CellController {
static final int DEFAULT_GRID_LINE_BREADTH = 1;
private static final String CELL_CONTROL_MARKER
= SpreadSheetLayout.class.getName() + "#Cell";
private final Composite spreadSheet;
private final Composite cellContainer;
private final SpreadSheetModel model;
private Label[] gridLines;
private final class ModelAdapter extends Adapter {
public void textChanged( final Event evt ) {
int row = evt.rowIndex;
int column = evt.columnIndex;
CellPosition position
= new CellPosition( row, column );
setText( evt.text, position );
}
}
CellController( final Composite spreadSheet,
final SpreadSheetModel model )
{
SpreadSheetUtils.checkNotNull( spreadSheet, "spreadSheet" );
SpreadSheetUtils.checkNotNull( model, "model" );
this.spreadSheet = spreadSheet;
cellContainer = new Composite( spreadSheet, SWT.NONE );
cellContainer.setBackground( spreadSheet.getBackground() );
this.model = model;
this.gridLines = new Label[ 0 ];
model.addListener( new ModelAdapter() );
}
void adjustCellControls( ) {
// TODO [fappel]: check the algorithm in relation to memory consumption
// and performance
cellContainer.moveBelow( null );
computeCellContainerBounds();
adjustGrid();
adjustCellLayoutData();
Map buffer = bufferExistingCells();
createMissingCells( buffer );
disposeOfSpareCells( buffer );
Rectangle clientArea = cellContainer.getClientArea();
Control[] children = cellContainer.getChildren();
for( int i = 0; i < children.length; i++ ) {
SpreadSheetUtils.computeBounds( clientArea, children[ i ], model );
}
updateText();
}
private void adjustGrid() {
adjustGridControlNumber();
int columnOffset = model.getColumnOffset();
int rowOffset = model.getRowOffset();
int xBase = SpreadSheetUtils.calcXPosition( 0, 0, columnOffset, model );
int yBase = SpreadSheetUtils.calcYPosition( 0, 0, rowOffset, model );
int gridLineIndex = 0;
for( int i = 1; i < model.getVisibleRows(); i++ ) {
int index = rowOffset + i;
int yPos = yBase;
yPos = SpreadSheetUtils.calcYPosition( yPos, rowOffset, index, model )
- DEFAULT_GRID_LINE_BREADTH;
int width = cellContainer.getClientArea().width;
Label line = gridLines[ gridLineIndex ];
line.setBounds( xBase, yPos, width, DEFAULT_GRID_LINE_BREADTH );
gridLineIndex++;
}
for( int i = 1; i < model.getVisibleColumns(); i++ ) {
int index = columnOffset + i;
int xPos = xBase;
xPos
= SpreadSheetUtils.calcXPosition( xBase, columnOffset, index, model )
- DEFAULT_GRID_LINE_BREADTH;
int height = cellContainer.getClientArea().height;
Label line = gridLines[ gridLineIndex ];
line.setBounds( xPos, yBase, DEFAULT_GRID_LINE_BREADTH, height );
gridLineIndex++;
}
}
private void adjustGridControlNumber() {
int visibleRows = model.getVisibleRows();
int visibleColumns = model.getVisibleColumns();
int newGridLineCount = Math.max( 0, visibleColumns + visibleRows - 2 );
Label[] newGridLines = new Label[ newGridLineCount ];
int linesToAdjust = Math.max( newGridLineCount, gridLines.length );
for( int i = 0; i < linesToAdjust; i++ ) {
if( i < gridLines.length && i < newGridLineCount ) {
// copy existing lines to new array for reuse
newGridLines[ i ] = gridLines[ i ];
} else if( i >= gridLines.length ) {
// the client area has been enlarged, so more lines are needed
newGridLines[ i ] = new Label( cellContainer, SWT.NONE );
newGridLines[ i ].setBackground( Graphics.getColor( 0, 0, 255 ) );
} else if( i >= newGridLineCount ) {
// the client area has been shrunken, so dispose of the spare ones.
gridLines[ i ].dispose();
}
}
gridLines = newGridLines;
}
private void adjustCellLayoutData() {
Set bufferedPositions = new HashSet();
Control[] children = cellContainer.getChildren();
for( int i = 0; i < children.length; i++ ) {
SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
if( data != null ) {
CellPosition position = data.getPosition();
bufferedPositions.add( position );
}
}
for( int i = 0; i < children.length; i++ ) {
SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
if( data != null ) {
CellPosition position = data.getPosition();
int oldRowIndex = position.getRowIndex();
int oldColumnIndex = position.getColumnIndex();
if( needsNewRowIndex( oldRowIndex )
|| needsNewColumnIndex( oldColumnIndex ) )
{
int rowIndex = calculateRowIndex( oldRowIndex );
int columnIndex = calculateColumn( oldColumnIndex );
CellPosition newPosition = new CellPosition( rowIndex, columnIndex );
if( !bufferedPositions.contains( newPosition ) ) {
SpreadSheetData newData = new SpreadSheetData( newPosition );
children[ i ].setLayoutData( newData );
}
}
}
}
}
private int calculateRowIndex( final int oldRowIndex ) {
int result = oldRowIndex;
if( needsNewRowIndex( oldRowIndex ) ) {
if( isInUpperOffset( oldRowIndex ) ) {
result = oldRowIndex + model.getVisibleRows();
} else {
result = oldRowIndex - model.getVisibleRows();
}
}
return result;
}
private int calculateColumn( final int oldColumnIndex ) {
int result = oldColumnIndex;
if( needsNewColumnIndex( oldColumnIndex ) ) {
if( isInLeftOffset( oldColumnIndex ) ) {
result = oldColumnIndex + model.getVisibleColumns();
} else {
result = oldColumnIndex - model.getVisibleColumns();
}
}
return result;
}
private boolean needsNewRowIndex( final int oldRowIndex ) {
return isInUpperOffset( oldRowIndex )
|| isInLowerOffset( oldRowIndex );
}
private boolean isInLowerOffset( final int oldRowIndex ) {
return oldRowIndex >= model.getVisibleRows() + model.getRowOffset();
}
private boolean isInUpperOffset( final int oldRowIndex ) {
return oldRowIndex < model.getRowOffset();
}
private boolean needsNewColumnIndex( final int oldColumnIndex ) {
return isInLeftOffset( oldColumnIndex )
|| isInRightOffset( oldColumnIndex );
}
private boolean isInRightOffset( final int oldColumnIndex ) {
int visibleColumns = model.getVisibleColumns();
return oldColumnIndex >= visibleColumns + model.getColumnOffset();
}
private boolean isInLeftOffset( final int oldColumnIndex ) {
return oldColumnIndex < model.getColumnOffset();
}
private void computeCellContainerBounds() {
Rectangle clientArea = spreadSheet.getClientArea();
int rowOffset = model.getRowOffset();
int yOffset = 0;
for( int i = 0; i < rowOffset; i++ ) {
yOffset += model.getRowHeight( i );
}
int columnOffset = model.getColumnOffset();
int xOffset = 0;
for( int i = 0; i < columnOffset; i++ ) {
xOffset += model.getColumnWidth( i );
}
cellContainer.setBounds( clientArea.x - xOffset,
clientArea.y - yOffset,
clientArea.width + xOffset,
clientArea.height + yOffset );
}
private void updateText() {
Control[] children = cellContainer.getChildren();
for( int i = 0; i < children.length; i++ ) {
if( children[ i ].getData( CELL_CONTROL_MARKER ) != null ) {
SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
CellPosition position = data.getPosition();
Label control = ( Label )children[ i ];
String text = model.getText( position );
control.setText( text );
}
}
}
Map bufferExistingCells() {
Control[] children = cellContainer.getChildren();
Map result = new HashMap();
for( int i = 0; i < children.length; i++ ) {
Control control = children[ i ];
if( isCellControl( control ) ) {
SpreadSheetData data = ( SpreadSheetData )control.getLayoutData();
result.put( data.getPosition(), control );
}
}
return result;
}
private void createMissingCells( final Map buffer )
{
int rowOffset = model.getRowOffset();
int rows = model.getVisibleRows() + rowOffset;
int columnOffset = model.getColumnOffset();
int columns = model.getVisibleColumns() + columnOffset;
for( int i = rowOffset; i < rows; i++ ) {
for( int j = columnOffset; j < columns; j++ ) {
CellPosition position = new CellPosition( i, j );
Control control = ( Control )buffer.get( position );
if( control == null ) {
if( model.getText( position ) != "" ) {
createCellControl( position );
}
}
}
}
}
private Control createCellControl( final CellPosition position )
{
Label result = new Label( cellContainer, SWT.NONE );
result.setLayoutData( new SpreadSheetData( position ) );
result.setData( CELL_CONTROL_MARKER, CELL_CONTROL_MARKER );
Display current = Display.getCurrent();
Color bgColor = current.getSystemColor( SWT.COLOR_WHITE );
result.setBackground( bgColor );
result.addMouseListener( new MouseAdapter() {
public void mouseUp( final MouseEvent evt ) {
SpreadSheet sheet = ( SpreadSheet )spreadSheet;
sheet.getCellEditorController().handleMouseUp( position );
}
public void mouseDown( final MouseEvent evt ) {
SpreadSheet sheet = ( SpreadSheet )spreadSheet;
sheet.getCellEditorController().handleMouseDown( position );
}
} );
return result;
}
static boolean isCellControl( final Control control ) {
Object data = control.getData( CELL_CONTROL_MARKER );
return control instanceof Label
&& control.getLayoutData() != null
&& CELL_CONTROL_MARKER.equals( data );
}
void setText( final String text,
final CellPosition position )
{
Label cell = findCellControl( position );
if( cell != null && "".equals( text ) ) {
cell.dispose();
} else if( cell != null && !"".equals( text ) ) {
cell.setText( text );
} else if( cell == null && !"".equals( text ) ) {
cell = ( Label )createCellControl( position );
Rectangle clientArea = cellContainer.getClientArea();
SpreadSheetUtils.computeBounds( clientArea, cell, model );
cell.setText( text );
}
}
private Label findCellControl( final CellPosition position )
{
Label result = null;
Control[] children = cellContainer.getChildren();
for( int i = 0; result == null && i < children.length; i++ ) {
SpreadSheetData data = ( SpreadSheetData )children[ i ].getLayoutData();
// TODO [fappel]: cellContainer should contain cell controls only
// -> check for cell control can be removed
if( data != null
&& position.equals( data.getPosition() )
&& isCellControl( children[ i ] ) )
{
result = ( Label )children[ i ];
}
}
return result;
}
void disposeOfSpareCells( final Map buffer )
{
int rowOffset = model.getRowOffset();
int rows = model.getVisibleRows() + rowOffset;
int columnOffset = model.getColumnOffset();
int columns = model.getVisibleColumns() + columnOffset;
for( int i = rowOffset; i < rows; i++ ) {
for( int j = columnOffset; j < columns; j++ ) {
CellPosition position = new CellPosition( i, j );
buffer.remove( position );
}
}
Iterator iterator = buffer.values().iterator();
while( iterator.hasNext() ) {
Control control = ( Control )iterator.next();
control.dispose();
}
}
Composite getCellContainer() {
return cellContainer;
}
Label[] getGridLines() {
return gridLines;
}
}