/*
* Copyright (C) 2005 David Orme <djo@coconut-palm-software.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* David Orme - Initial API and implementation
* The Pampered Chef - Expanded to handle sorting
* Elias Volanakis - 267316
*/
package org.eclipse.swt.nebula.widgets.compositetable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
/**
* Class AbstractHeader. A Header class making it easier to implement a
* sorted table where clicking on a header column sets or changes the sort
* order.
*
* @author djo
*/
public abstract class AbstractNativeHeader extends Composite {
private String[] columnLabelStrings;
private int[] columnAlignments;
Table headerTable;
List tableColumns;
private HeaderLayout headerLayout;
private int sortDirection = SWT.NONE; // SWT.NONE, SWT.UP, SWT.DOWN
private int lastSortColumn = -1;
// Begin main implementation-----------------------------------------------
/**
* A Header object for CompositeTable that can tell clients to re-sort,
* and can move/resize the columns (if used with an appropriate layout
* manager).
*
* @param parent The SWT parent
* @param style SWT style bits. The same style bits accepted by Composite.
*/
public AbstractNativeHeader(Composite parent, int style) {
super(parent, style);
headerTable = new Table(this, SWT.NULL);
headerTable.setHeaderVisible(true);
addControlListener(controlListener);
addDisposeListener(disposeListener);
headerLayout = new HeaderLayout();
setLayout(headerLayout);
}
/**
* Clients must override this method to reset the current sort column/order
* if they want to support sorting.
*
* @param column
* The column on which to sort.
* @param sortDirection
* SWT.NONE, SWT.UP, or SWT.DOWN.
* @return boolean true if sorting occurred; false otherwise.
*/
protected boolean sortOnColumn(int column, int sortDirection) {
return false;
}
private boolean movable = true;
/**
* Sets if the columns are movable.
*
* @param movable true if the columns are movable; false otherwise.
*/
public void setMovable(boolean movable) {
this.movable = movable;
for (Iterator columnsIter = tableColumns.iterator(); columnsIter.hasNext();) {
TableColumn column = (TableColumn) columnsIter.next();
column.setMoveable(movable);
}
}
/**
* Returns if the columns are movable.
*
* @return boolean true if the columns are movable; false otherwise.
*/
public boolean isMovable() {
return movable;
}
private boolean resizable = true;
/**
* Sets if the columns are resizable.
*
* @param resizable true if the columns should be resizable; false otherwise.
*/
public void setResizable(boolean resizable) {
this.resizable = resizable;
for (Iterator columnsIter = tableColumns.iterator(); columnsIter.hasNext();) {
TableColumn column = (TableColumn) columnsIter.next();
column.setResizable(resizable);
}
}
/**
* Returns if the columns are resizable.
*
* @return boolean true if the columns should be resizable; false otherwise.
*/
public boolean isResizable() {
return resizable;
}
/**
* Clients must call this method (or its overloaded cousin) exactly once
* in their constructor to set the column text strings. All alignments
* will be set to SWT.LEFT.
*
* @param columnTextStrings String[] The text strings to display in each column
*/
public void setColumnText(String[] columnTextStrings) {
this.columnLabelStrings = columnTextStrings;
this.columnAlignments = new int[columnLabelStrings.length];
for (int i = 0; i < columnAlignments.length; i++) {
columnAlignments[i] = SWT.LEFT;
}
initializeColumns();
}
/**
* Clients must call this method (or its overloaded cousin) exactly once
* in their constructor to set the column text strings and alignments.
*
* @param columnTextStrings String[] The text strings to display in each column
* @param alignments An array of SWT style bits. Each element is one of:
* SWT.LEFT, SWT.CENTER, or SWT.RIGHT.
*/
public void setColumnText(String[] columnTextStrings, int[] alignments) {
this.columnLabelStrings = columnTextStrings;
this.columnAlignments = alignments;
initializeColumns();
}
public TableColumn[] getColumns() {
return headerTable.getColumns();
}
/**
* Sets the sort indicator onto the specified column.
*
* @param index the 0-based column index or -1 if no column is sorted
* @see #setSortDirection(int)
*/
public void setSortColumn(int index) {
if (index == -1) {
headerTable.setSortColumn(null);
} else {
TableColumn column = headerTable.getColumn(index);
headerTable.setSortColumn(column);
}
lastSortColumn = index;
}
/**
* The index of the currently sorted table column
*
* @return a 0-based index or -1 if no column is sorted
*/
public int indexOfSortColumn() {
TableColumn column = headerTable.getSortColumn();
return column == null ? -1 : headerTable.indexOf(column);
}
/**
* Set the sort direction.
*
* @param direct one of SWT.UP, SWT.DOWN, SWT.NONE
* @throws RuntimeException if direction has an invalid value
* @see #setSortColumn(int);
*/
public void setSortDirection(int direction) {
if (!(direction == SWT.NONE || direction == SWT.UP || direction == SWT.DOWN)) {
throw new IllegalArgumentException("direction= " + direction);
}
headerTable.setSortDirection(direction);
sortDirection = direction;
}
/**
* Returns the current sort direction.
*
* @return one of SWT.UP, SWT.DOWN, SWT.NONE
*/
public int getSortDirection() {
return headerTable.getSortDirection();
}
private List columnControlListeners = new ArrayList();
public void addColumnControlListener(ControlListener c) {
columnControlListeners.add(c);
}
public void removeColumnControlListener(ControlListener c) {
columnControlListeners.remove(c);
}
private void initializeColumns() {
this.tableColumns = new ArrayList();
for (int i = 0; i < columnLabelStrings.length; i++) {
TableColumn column = new TableColumn(headerTable, columnAlignments[i]);
column.setMoveable(movable);
column.setResizable(resizable);
column.setText(columnLabelStrings[i]);
this.tableColumns.add(column);
column.addControlListener(columnControlListener);
column.addSelectionListener(columnSelectionListener);
}
}
private int toggleSortDirection() {
if (sortDirection == SWT.NONE) {
sortDirection = SWT.DOWN;
} else if (sortDirection == SWT.DOWN) {
sortDirection = SWT.UP;
} else if (sortDirection == SWT.UP) {
sortDirection = SWT.DOWN;
}
return sortDirection;
}
public Point computeSize(int wHint, int hHint) {
return computeSize(wHint, hHint, true);
}
public Point computeSize(int wHint, int hHint, boolean changed) {
Point preferredSize = super.computeSize(wHint, hHint, changed);
preferredSize.y = headerTable.getHeaderHeight();
return preferredSize;
}
/**
* Method getWeights. If isFittingHorizontally, returns an array
* representing the percentage of the total width each column is allocated
* or null if no weights have been specified.
* <p>
* If !isFittingHorizontally, returns an array where each element is the
* minimum width in pixels of the corresponding column.
*
* @return the current weights array or null if no weights have been
* specified.
*/
public int[] getWeights() {
return headerLayout.getWeights();
}
/**
* Method setWeights. If isFittingHorizontally, specifies an array
* representing the percentage of the total width each column is allocated
* or null if no weights have been specified.
* <p>
* If !isFittingHorizontally, specifies an array where each element is the
* minimum width in pixels of the corresponding column.
* <p>
* This property is ignored if the programmer has set a layout manager on
* the header and/or the row prototype objects.
* <p>
* The number of elements in the array must match the number of columns and
* if isFittingHorizontally, the sum of all elements must equal 100. If
* either of these constraints is not true, this property will be ignored
* and all columns will be created equal in width.
*
* @param weights
* the weights to use if the CompositeTable is automatically
* laying out controls.
* @return this
*/
public AbstractNativeHeader setWeights(int[] weights) {
headerLayout.setWeights(weights);
return this;
}
/**
* Method isFittingHorizontally. Returns if the CompositeTable control will
* scale the widths of all columns so that they all fit into the available
* space. The default value is false.
*
* @return Returns true if the table's actual width is set to equal the
* visible width; false otherwise.
*/
public boolean isFittingHorizontally() {
return headerLayout.isFittingHorizontally();
}
/**
* Method setFittingHorizontally. Sets if the CompositeTable control will
* scale the widths of all columns so that they all fit into the available
* space. The default value is false.
*
* @param fittingHorizontally
* true if the table's actual width is set to equal the visible
* width; false otherwise.
* @return this
*/
public AbstractNativeHeader setFittingHorizontally(boolean fittingHorizontally) {
headerLayout.setFittingHorizontally(fittingHorizontally);
return this;
}
// Event handlers ---------------------------------------------------------
private ControlListener controlListener = new ControlAdapter() {
public void controlResized(ControlEvent e) {
Point size = AbstractNativeHeader.this.getSize();
headerTable.setBounds(0, 0, size.x, size.y);
}
};
private ControlListener columnControlListener = new ControlAdapter() {
public void controlResized(ControlEvent e) {
for (Iterator i = columnControlListeners.iterator(); i.hasNext();) {
ControlListener listener = (ControlListener) i.next();
listener.controlResized(e);
}
}
public void controlMoved(ControlEvent e) {
for (Iterator i = columnControlListeners.iterator(); i.hasNext();) {
ControlListener listener = (ControlListener) i.next();
listener.controlMoved(e);
}
}
};
private SelectionListener columnSelectionListener = new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
public void widgetSelected(SelectionEvent e) {
TableColumn tableColumn = (TableColumn)e.widget;
int c = tableColumns.indexOf(tableColumn);
if (c != lastSortColumn) {
sortDirection = SWT.NONE;
}
if (sortOnColumn(c, toggleSortDirection())) {
if (c != lastSortColumn) headerTable.setSortColumn(tableColumn);
headerTable.setSortDirection(sortDirection);
lastSortColumn = c;
}
}
};
private DisposeListener disposeListener = new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
AbstractNativeHeader header = AbstractNativeHeader.this;
header.removeControlListener(controlListener);
}
};
}