/**************************************************************************** * Copyright (c) 2005-2006 Jeremy Dowdall * 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: * Jeremy Dowdall <jeremyd@aspencloud.com> - initial API and implementation *****************************************************************************/ package org.eclipse.nebula.widgets.ctree; import java.util.Iterator; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Layout; /** * <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> */ class CTreeLayout extends Layout { /** * true if the platform is detected as being "carbon" */ public static final boolean carbon = "carbon".equals(SWT.getPlatform()); /** * true if the platform is detected as being "gtk" */ public static final boolean gtk = "gtk".equals(SWT.getPlatform()); /** * true if the platform is detected as being "win32" */ public static final boolean win32 = "win32".equals(SWT.getPlatform()); /** * the CTree */ protected CTree ctree; /** * the size of the container's header composite */ protected Point headerSize = new Point(0,0); /** * the height of the container contents */ protected int contentHeight = 0; private Point size = new Point(0,0); int itemHeight = CTree.gtk ? 25 : 20; private boolean itemHeightSet = false; private boolean fixedHeight = false; /** * @param ctree */ public CTreeLayout(CTree ctree) { this.ctree = ctree; } /** * Compute the height of the Content area without consideration for cached values. */ protected void computeContentHeight() { if(ctree.isEmpty()) { contentHeight = 0; } else { int gridLine = ctree.getGridLineWidth(); List vitems = ctree.items(false); List pitems = ctree.getPaintedItems(); contentHeight = (vitems.size() - pitems.size()) * getItemHeight(); for(Iterator i = pitems.iterator(); i.hasNext(); ) { contentHeight += ((CTreeItem) i.next()).computeHeight(); contentHeight += gridLine; } contentHeight -= gridLine; } } /** * Compute the height of the Content area after the given cell has experienced an event of * the given type. * <p> * Event types: * <ul> * <li>SWT.Collapse</li> * <li>SWT.Expand</li> * <li>SWT.Resize</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param cell */ protected void computeContentHeight(int eventType, CTreeCell cell) { computeContentHeight(); } /** * Compute the height of the Content area after the given column has experienced an event of * the given type. * <p> * Event types: * <ul> * <li>SWT.Move</li> * <li>SWT.Resize</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param column */ protected void computeContentHeight(int eventType, CTreeColumn column) { computeContentHeight(); } /** * Compute the height of the Content area after the given item has experienced an event of * the given type. * <p> * Event types: * <ul> * <li>SWT.Hide</li> * <li>SWT.Move</li> * <li>SWT.Show</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param item */ protected void computeContentHeight(int eventType, CTreeItem item) { computeContentHeight(); } private void computeHeaderSize() { headerSize.x = headerSize.y = 0; int gridLine = ctree.getGridLineWidth(); CTreeColumn[] columns = ctree.getColumns(); for(int i = 0; i < columns.length; i++) { if(!columns[i].getAutoWidth()) { headerSize.x += columns[i].getWidth(); } headerSize.x += gridLine; } headerSize.y = ctree.getHeaderHeight(); } protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { computeHeaderSize(); computeContentHeight(); size.x = headerSize.x; size.y = headerSize.y + contentHeight; Point returnSize = new Point(size.x, size.y); if(ctree.hBar != null && !ctree.hBar.isDisposed() && !ctree.hBar.getVisible()) { returnSize.y -= (ctree.hBar.getSize().y + 3); } if(ctree.vBar != null && !ctree.vBar.isDisposed() && !ctree.vBar.getVisible()) { returnSize.x -= (ctree.vBar.getSize().x + 3); } return returnSize; } private void computeSize(int eventType, CTreeCell cell) { computeContentHeight(eventType, cell); size.x = headerSize.x; size.y = headerSize.y + contentHeight; } private void computeSize(int eventType, CTreeColumn column) { if(SWT.Resize == eventType) { computeHeaderSize(); computeContentHeight(eventType, column); size.x = headerSize.x; size.y = headerSize.y + contentHeight; } } private void computeSize(int eventType, CTreeItem item) { if(SWT.Move != eventType) { computeContentHeight(eventType, item); size.x = headerSize.x; size.y = headerSize.y + contentHeight; } } int getItemHeight() { if(itemHeightSet) return itemHeight; if(ctree.isEmpty()) return itemHeight; itemHeight = ((CTreeItem) ctree.itemList.get(0)).computeHeight(); itemHeightSet = true; return itemHeight; } protected void layout(Composite composite, boolean flushCache) { Rectangle area = composite.getClientArea(); if(area.isEmpty()) return; if(flushCache) computeSize(composite, -1, -1, flushCache); ctree.getHeader().setBounds( area.x, area.y, area.width, headerSize.y ); Point origin = ctree.getScrollPosition(); int height = area.height-headerSize.y; ctree.body.setBounds( area.x-origin.x, area.y+headerSize.y-origin.y, (gtk ? Math.max(area.width, headerSize.x) : area.width), (gtk ? Math.max(height, contentHeight) : height) ); layoutHeader(); layoutContent(); updateScrollBars(); } /** * Layout the Content area after the given cell has experienced an event of the given type. * <p> * Event types: * <ul> * <li>SWT.Collapse</li> * <li>SWT.Expand</li> * <li>SWT.Resize</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param cell */ void layout(int eventType, CTreeCell cell) { computeSize(eventType, cell); layoutContent(eventType, cell); updateScrollBars(); } /** * Layout the Content area after the given column has experienced an event of the given type. * <p> * Event types: * <ul> * <li>SWT.Move</li> * <li>SWT.Resize</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param column */ void layout(int eventType, CTreeColumn column) { if(SWT.Resize == eventType) { computeSize(eventType, column); if(gtk) { ctree.body.setSize( Math.max(ctree.getClientArea().width, headerSize.x), ctree.body.getSize().y ); } layoutContent(eventType, column); updateScrollBars(); } } /** * Layout the Content area after the given item has experienced an event of the given type. * Because an event has occurred, indicating a change in content, computeSize will be called. * <p> * Event types: * <ul> * <li>SWT.Collapse</li> * <li>SWT.Expand</li> * <li>SWT.Hide</li> * <li>SWT.Move</li> * <li>SWT.Show</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param item */ void layout(int eventType, CTreeItem item) { if(SWT.Move != eventType) { computeSize(eventType, item); } layoutContent(eventType, item); if(SWT.Move != eventType) { updateScrollBars(); } } /** * Compute the height of the Content area without consideration for cached values. */ protected void layoutContent() { int gridLine = ctree.getGridLineWidth(); int top = 0; for(Iterator i = ctree.items(false).iterator(); i.hasNext(); ) { CTreeItem item = (CTreeItem) i.next(); item.setTop(top); item.setHeight(fixedHeight ? getItemHeight() : item.computeHeight()); top += item.getHeight() + gridLine; } ctree.body.redraw(); } /** * Layout the Content area after the given cell has experienced an event of the given type. * <p> * Event types: * <ul> * <li>SWT.Collapse</li> * <li>SWT.Expand</li> * <li>SWT.Resize</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param cell */ protected void layoutContent(int eventType, CTreeCell cell) { layoutContent(); } /** * Layout the Content area after the given column has experienced an event of the given type. * <p> * Event types: * <ul> * <li>SWT.Move</li> * <li>SWT.Resize</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param column */ protected void layoutContent(int eventType, CTreeColumn column) { layoutContent(); } /** * Layout the Content area after the given item has experienced an event of the given type. * This method is called after any necessary call to computeSize. * <p> * Event types: * <ul> * <li>SWT.Hide</li> * <li>SWT.Move</li> * <li>SWT.Show</li> * </ul> * </p> * Note that subclasses do not have to handle all types and may also implement more of necessary. * @param eventType * @param item */ protected void layoutContent(int eventType, CTreeItem item) { layoutContent(); } /** * layout the header. will recalcuate and resize any autowidth columns if necessary */ protected void layoutHeader() { if(ctree.nativeHeader) { Rectangle tBounds = ctree.getHeader().getClientArea(); tBounds.height += 30; // TODO: does this value matter, as long as it's > the scrollbar size? // pogramatic scrolling doesn't work on win32 if(CTree.win32 && ctree.hBar != null) { int sel = ctree.hBar.getSelection(); tBounds.x = -sel; tBounds.width += sel; } ctree.internalTable.setBounds(tBounds); } int num_fillers = 0; CTreeColumn[] columns = ctree.getColumns(); for(int i = 0; i < columns.length; i++) { if(columns[i].getAutoWidth()) { num_fillers++; } } if(num_fillers > 0) { int[] fillers = new int[num_fillers]; int xconsumed = 0; for(int i = 0, j = 0; i < columns.length && j < num_fillers; i++) { if(columns[i].getAutoWidth()) { fillers[j++] = i; } else { xconsumed += columns[i].getWidth(); } } int headerWidth = ctree.getHeader().getSize().x; int fillerWidth0 = (headerWidth - xconsumed) / num_fillers; int fillerWidth1 = headerWidth - xconsumed - (fillerWidth0 * (num_fillers - 1)); if(fillerWidth0 == 0) fillerWidth0 = 1; if(fillerWidth1 == 0) fillerWidth1 = 1; for(int i = 0; i < num_fillers; i++) { if(i == num_fillers-1) { columns[fillers[i]].setWidth(fillerWidth1); } else { columns[fillers[i]].setWidth(fillerWidth0); } } } } void setItemHeight(int height) { itemHeight = height; itemHeightSet = true; } /** * Recalculate the correct state of the scrollbars and set their visibility accordingly. */ private void updateScrollBars() { Rectangle area = ctree.getClientArea(); if(area.isEmpty()) return; if(ctree.hBar != null) { if(area.width < size.x) { ctree.hBar.setMaximum(size.x); ctree.hBar.setThumb(area.width); if(!ctree.hBar.getVisible()) { ctree.hBar.setVisible(true); } } else { ctree.hBar.setMaximum(1); if(ctree.hBar.getVisible()) { ctree.hBar.setVisible(false); } } } if(ctree.vBar != null) { if(area.height < size.y) { ctree.vBar.setMaximum(size.y); ctree.vBar.setThumb(area.height); if(!ctree.vBar.getVisible()) { ctree.vBar.setVisible(true); } } else { ctree.vBar.setMaximum(1); if(ctree.vBar.getVisible()) { ctree.vBar.setVisible(false); } } } } }