package org.eclipse.swt.nebula.widgets.compositetable;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Widget;
/**
* ResizableGridRowLayout works with HeaderLayout to implement column resizing
* semantics for CompositeTable UIs.
* <p>
* Use a ResizableGridRowLayout when you have used a ResizableGridHeaderLayout
* on the Header object. ResizableGridRowLayout gets all of its layout settings
* from the ResizableGridHeaderLayout object, so there is no need to set any
* additional layout information on the ResizableGridRowLayout itself.
*
* @author djo
*/
public class ResizableGridRowLayout extends GridRowLayout {
private AbstractGridRowLayout delegate = null;
private int[] columnOrder;
private Composite control;
/**
* Constructor ResizableGridRowLayout. Create a ResizableGridRowLayout
* object. Since a ResizableGridRowLayout object will automatically find
* the associated HeaderLayout, no properties need to be set on a
* ResizableGridRowLayout.
*/
public ResizableGridRowLayout() {}
protected Point computeSize(Composite composite, int wHint, int hHint,
boolean flushCache) {
this.control = composite;
getLayoutDelegate(composite);
return super.computeSize(composite, wHint, hHint, flushCache);
}
protected void layout(Composite composite, boolean flushCache) {
this.control = composite;
getLayoutDelegate(composite);
super.layout(composite, flushCache);
}
public int[] getWeights() {
if (delegate == null) {
return super.getWeights();
}
return delegate.getWeights();
}
public AbstractGridRowLayout setWeights(int[] weights) {
if (delegate == null) {
return super.setWeights(weights);
}
return delegate.setWeights(weights);
}
public int getSumOfAllWeights() {
if (delegate == null) {
return super.getSumOfAllWeights();
}
return delegate.getSumOfAllWeights();
}
public boolean isFittingHorizontally() {
if (delegate == null) {
return super.isFittingHorizontally();
}
return delegate.isFittingHorizontally();
}
public AbstractGridRowLayout setFittingHorizontally(boolean fittingHorizontally) {
if (delegate == null) {
return super.setFittingHorizontally(fittingHorizontally);
}
return delegate.setFittingHorizontally(fittingHorizontally);
}
protected Widget getColumnAt(Composite rowOrHeader, int offset) {
if (columnOrder == null) {
return super.getColumnAt(rowOrHeader, offset);
}
Control[] children = rowOrHeader.getChildren();
return children[columnOrder[offset]];
}
private CompositeTableLayout getLayoutDelegate(Composite composite) {
if (delegate != null) return delegate;
findHeader(composite);
if (delegate != null) {
return delegate;
}
createNullLayout(composite);
return delegate;
}
private void createNullLayout(Composite composite) {
int numChildren = composite.getChildren().length;
delegate = new GridRowLayout(new int[numChildren], true);
}
private void findHeader(Composite row) {
Control[] children = row.getParent().getChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Composite) {
Composite child = (Composite) children[i];
Layout layout = child.getLayout();
if (layout instanceof HeaderLayout) {
delegate = (HeaderLayout) layout;
addListenersToDelegate(row, (HeaderLayout) layout);
return;
}
}
}
}
private void addListenersToDelegate(Composite row, HeaderLayout delegate) {
delegate.addColumnControlListener(new GridColumnControlListener(row));
}
private class GridColumnControlListener extends ColumnControlListener {
private final Composite row;
private int savedResizedColNum;
private int savedResizedColWidth;
private int savedColToRightOfResizedColWidth;
boolean runnableQueueIsClear = true;
public GridColumnControlListener(Composite row) {
this.row = row;
}
public void columnMoved(int[] newColumnOrder) {
ResizableGridRowLayout.this.columnOrder = newColumnOrder;
control.layout(true);
}
public void columnResized(int resizedColumnPosition,
int resizedColumnWidth,
int columnToTheRightOfResizedColumnWidth)
{
/*
* FEATURE IN WINDOWS: If we resize the row immediately, Windows
* won't allow the row to repaint, resulting in cheese (ugly
* draw artifacts) all over the place until the user releases the
* mouse button. The workaround is to resize the row columns in the
* next idle event during an async runnable. Since this code will
* run on every mouseMove event, we are careful to queue at most one
* async runnable at a time.
*/
this.savedResizedColNum = resizedColumnPosition;
this.savedResizedColWidth = resizedColumnWidth;
this.savedColToRightOfResizedColWidth = columnToTheRightOfResizedColumnWidth;
if (runnableQueueIsClear) {
runnableQueueIsClear = false;
row.getDisplay().asyncExec(new Runnable() {
public void run() {
if (row.isDisposed()) return;
// formerly: Control[] children = row.getChildren();
// --> keep side-effects assignments (?)
row.getChildren();
Control resizedColumn = (Control) getColumnAt(row, savedResizedColNum);
// Control resizedColumn = children[savedResizedColNum];
Point resizedColumnSize = resizedColumn.getSize();
int adjustedResizedColumnWidth = savedResizedColWidth - 2 *
CELL_BORDER_WIDTH;
int resizedColumnWidthChange = adjustedResizedColumnWidth
- resizedColumnSize.x;
resizedColumn.setSize(adjustedResizedColumnWidth,
resizedColumnSize.y);
Control columnToTheRightOfResizedColumn = (Control) getColumnAt(row, savedResizedColNum+1);
// Control columnToTheRightOfResizedColumn = children[savedResizedColNum + 1];
Rectangle rightBounds = columnToTheRightOfResizedColumn.getBounds();
columnToTheRightOfResizedColumn.setBounds(rightBounds.x
+ resizedColumnWidthChange, rightBounds.y,
savedColToRightOfResizedColWidth,
rightBounds.height);
runnableQueueIsClear = true;
}
});
}
}
};
}