/******************************************************************************* * Copyright (c) 2006 IBM Corporation and others. * 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: * chris.gross@us.ibm.com - initial API and implementation * mario.winterer@scch.at - bugfix in 244333 * Marty Jones<martybjones@gmail.com> - custom header/footer font in bug 293743 *******************************************************************************/ package org.eclipse.nebula.widgets.grid; import org.eclipse.nebula.widgets.grid.internal.DefaultColumnGroupHeaderRenderer; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.TreeListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.TypedListener; import java.util.Vector; /** * <p> * NOTE: THIS WIDGET AND ITS API ARE STILL UNDER DEVELOPMENT. THIS IS A PRE-RELEASE ALPHA * VERSION. USERS SHOULD EXPECT API CHANGES IN FUTURE VERSIONS. * </p> * Instances of this class represent a column group in a grid widget. A column group header is * displayed above grouped columns. The column group can optionally be configured to expand and * collapse. A column group in the expanded state shows {@code GridColumn}s whose detail property * is true. A column group in the collapsed state shows {@code GridColumn}s whose summary property * is true. * <p> * <dl> * <dt><b>Styles:</b></dt> * <dd>SWT.TOGGLE</dd> * <dt><b>Events:</b></dt> * <dd>Expand, Collapse</dd> * </dl> * * @author chris.gross@us.ibm.com */ public class GridColumnGroup extends Item { private Grid parent; private GridColumn[] columns = new GridColumn[] {}; private boolean expanded = true; private Font headerFont; /** * Header renderer. */ private GridHeaderRenderer headerRenderer = new DefaultColumnGroupHeaderRenderer(); /** * Constructs a new instance of this class given its parent (which must be a Table) and a style * value describing its behavior and appearance. * * @param parent the parent table * @param style the style of the group * @throws IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @throws org.eclipse.swt.SWTException * <ul> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the parent</li> * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> * </ul> */ public GridColumnGroup(Grid parent, int style) { super(parent, style); this.parent = parent; headerRenderer.setDisplay(getDisplay()); parent.newColumnGroup(this); } /** * Adds the listener to the collection of listeners who will * be notified when an item in the receiver is expanded or collapsed * by sending it one of the messages defined in the <code>TreeListener</code> * interface. * * @param listener the listener which should be notified * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see TreeListener * @see #removeTreeListener */ public void addTreeListener(TreeListener listener) { checkWidget (); if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); TypedListener typedListener = new TypedListener (listener); addListener (SWT.Expand, typedListener); addListener (SWT.Collapse, typedListener); } /** * Removes the listener from the collection of listeners who will * be notified when items in the receiver are expanded or collapsed. * * @param listener the listener which should no longer be notified * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see TreeListener * @see #addTreeListener */ public void removeTreeListener(TreeListener listener) { checkWidget (); if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); removeListener (SWT.Expand, listener); removeListener (SWT.Collapse, listener); } /** * Returns the parent grid. * * @throws org.eclipse.swt.SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> */ public Grid getParent() { checkWidget(); return parent; } int getNewColumnIndex() { if (columns.length == 0) { return -1; } GridColumn lastCol = columns[columns.length - 1]; return parent.indexOf(lastCol) + 1; } void newColumn(GridColumn column, int index) { GridColumn[] newAllColumns = new GridColumn[columns.length + 1]; System.arraycopy(columns, 0, newAllColumns, 0, columns.length); newAllColumns[newAllColumns.length - 1] = column; columns = newAllColumns; } void removeColumn(GridColumn col) { if (columns.length == 0) return; // widget is disposing GridColumn[] newAllColumns = new GridColumn[columns.length - 1]; int x = 0; for (int i = 0; i < columns.length; i++) { if (columns[i] != col) { newAllColumns[x] = columns[i]; x ++; } } columns = newAllColumns; } /** * Returns the columns within this group. * <p> * Note: This is not the actual structure used by the receiver to maintain * its list of items, so modifying the array will not affect the receiver. * </p> * @return the columns * @throws org.eclipse.swt.SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> */ public GridColumn[] getColumns() { checkWidget(); GridColumn[] newArray = new GridColumn[columns.length]; System.arraycopy (columns, 0, newArray, 0, columns.length); return newArray; } /** * {@inheritDoc} */ public void dispose() { super.dispose(); if (parent.isDisposing()) return; GridColumn[] oldColumns = columns; columns = new GridColumn[0]; for (int i = 0; i < oldColumns.length; i++) { oldColumns[i].dispose(); } parent.removeColumnGroup(this); } /** * Gets the header renderer. * * @throws org.eclipse.swt.SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> */ public GridHeaderRenderer getHeaderRenderer() { checkWidget(); return headerRenderer; } /** * Sets the header renderer. * * @param headerRenderer The headerRenderer to set. * @throws IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the renderer is null</li> * </ul> * @throws org.eclipse.swt.SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> */ public void setHeaderRenderer(GridHeaderRenderer headerRenderer) { if (headerRenderer == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); this.headerRenderer = headerRenderer; headerRenderer.setDisplay(getDisplay()); } /** * Returns true if the receiver is expanded, false otherwise. * * @return the expanded attribute * @throws org.eclipse.swt.SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> */ public boolean getExpanded() { checkWidget(); return expanded; } /** * Sets the expanded state of the receiver. * * @param expanded the expanded to set * @throws org.eclipse.swt.SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> */ public void setExpanded(boolean expanded) { checkWidget(); this.expanded = expanded; if (!expanded && getParent().getCellSelectionEnabled()) { Vector collapsedCols = new Vector(); for (int j = 0; j < columns.length; j++) { if (!columns[j].isSummary()) { collapsedCols.add(new Integer(getParent().indexOf(columns[j]))); } } Point[] selection = getParent().getCellSelection(); for (int i = 0; i < selection.length; i++) { if (collapsedCols.contains(new Integer(selection[i].x))) { getParent().deselectCell(selection[i]); } } if (getParent().getFocusColumn() != null && getParent().getFocusColumn().getColumnGroup() == this) { getParent().updateColumnFocus(); } parent.updateColumnSelection(); } if (parent.getCellSelectionEnabled()) parent.refreshHoverState(); parent.setScrollValuesObsolete(); parent.redraw(); } /** * Returns the first visible column in this column group. * * @return first visible column */ GridColumn getFirstVisibleColumn() { GridColumn[] cols = parent.getColumnsInOrder(); for (int i = 0; i < cols.length; i++) { if (cols[i].getColumnGroup() == this && cols[i].isVisible()) { return cols[i]; } } return null; } /** * Returns the last visible column in this column group. * * @return last visible column */ GridColumn getLastVisibleColumn() { GridColumn[] cols = parent.getColumnsInOrder(); GridColumn lastVisible = null; for (int i = 0; i < cols.length; i++) { if (cols[i].getColumnGroup() == this && cols[i].isVisible()) { lastVisible = cols[i]; } } return lastVisible; } Rectangle getBounds() { Rectangle bounds = new Rectangle(0, 0, 0, 0); bounds.height = parent.getGroupHeaderHeight(); boolean foundFirstColumnInGroup = false; GridColumn[] cols = parent.getColumnsInOrder(); for (int i = 0; i < cols.length; i++) { if (cols[i].getColumnGroup() == this) { if (cols[i].isVisible()) { if (!foundFirstColumnInGroup) { bounds.x = parent.getOrigin(cols[i], null).x; foundFirstColumnInGroup = true; } bounds.width += cols[i].getWidth(); } } else { if (foundFirstColumnInGroup) { break; } } } return bounds; } /** * Sets whether or not text is word-wrapped in the header for this column group. If Grid.setAutoHeight(true) is set, the row height * is adjusted to accommodate word-wrapped text. * @param wordWrap Set to true to wrap the text, false otherwise * @see #getHeaderWordWrap() */ public void setHeaderWordWrap(boolean wordWrap) { checkWidget(); headerRenderer.setWordWrap(wordWrap); parent.redraw(); } /** * Returns whether or not text is word-wrapped in the header for this column group. * @return true if the header wraps its text. * @see GridColumn#setHeaderWordWrap(boolean) */ public boolean getHeaderWordWrap() { checkWidget(); return headerRenderer.isWordWrap(); } /** * Returns the font that the receiver will use to paint textual information * for the header. * * @return the receiver's font * @throws SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> */ public Font getHeaderFont() { checkWidget(); if (headerFont == null) { return parent.getFont(); } return headerFont; } /** * Sets the Font to be used when displaying the Header text. * @param font */ public void setHeaderFont(Font font) { checkWidget(); this.headerFont = font; } }