/******************************************************************************* * Copyright (c) 2000, 2015 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: * IBM Corporation - initial API and implementation * EclipseSource - ongoing development *******************************************************************************/ package org.eclipse.swt.widgets; import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.internal.widgets.ICoolBarAdapter; import org.eclipse.swt.internal.widgets.IItemHolderAdapter; import org.eclipse.swt.internal.widgets.coolbarkit.CoolBarLCA; /** * Instances of this class provide an area for dynamically positioning the items * they contain. * <p> * The item children that may be added to instances of this class must be of * type <code>CoolItem</code>. * </p> * <p> * Note that although this class is a subclass of <code>Composite</code>, it * does not make sense to add <code>Control</code> children to it, or set a * layout on it. * </p> * <p> * <dl> * <dt><b>Styles:</b></dt> * <dd>FLAT, HORIZONTAL, VERTICAL</dd> * <dt><b>Events:</b></dt> * <dd>(none)</dd> * </dl> * </p> * <p> * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. * </p> * <p> * IMPORTANT: This class is <em>not</em> intended to be subclassed. * </p> * @since 1.0 */ public class CoolBar extends Composite { private class CoolBarAdapter implements ICoolBarAdapter { @Override public void setItemOrder( int[] itemOrder ) { CoolBar.this.setItemOrder( itemOrder ); } } private class CoolBarItemHolder implements IItemHolderAdapter<Item> { @Override public void add( Item item ) { } @Override public Item[] getItems() { return CoolBar.this.getItems(); } @Override public void insert( Item item, int index ) { } @Override public void remove( Item item ) { } } CoolItem[][] items = new CoolItem[0][0]; CoolItem[] originalItems = new CoolItem[0]; // Cursor hoverCursor, dragCursor, cursor; CoolItem dragging = null; int mouseXOffset, itemXOffset; boolean isLocked = false; boolean inDispose = false; static final int ROW_SPACING = 2; static final int CLICK_DISTANCE = 3; static final int DEFAULT_COOLBAR_WIDTH = 0; static final int DEFAULT_COOLBAR_HEIGHT = 0; private transient IItemHolderAdapter<Item> itemHolder; private transient ICoolBarAdapter coolBarAdapter; /** * Constructs a new instance of this class given its parent and a style value * describing its behavior and appearance. * <p> * The style value is either one of the style constants defined in class * <code>SWT</code> which is applicable to instances of this class, or must * be built by <em>bitwise OR</em>'ing together (that is, using the * <code>int</code> "|" operator) two or more of those <code>SWT</code> * style constants. The class description lists the style constants that are * applicable to the class. Style bits are also inherited from superclasses. * </p> * * @param parent * a composite control which will be the parent of the new instance * (cannot be null) * @param style * the style of control to construct * * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @exception 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> * * @see SWT * @see Widget#checkSubclass * @see Widget#getStyle */ public CoolBar( Composite parent, int style ) { super( parent, checkStyle( style ) ); if ( (style & SWT.VERTICAL) != 0 ) { this.style |= SWT.VERTICAL; // hoverCursor = new Cursor(display, SWT.CURSOR_SIZENS); } else { this.style |= SWT.HORIZONTAL; // hoverCursor = new Cursor(display, SWT.CURSOR_SIZEWE); } // dragCursor = new Cursor(display, SWT.CURSOR_SIZEALL); Listener listener = new Listener() { @Override public void handleEvent( Event event ) { switch (event.type) { case SWT.Dispose: onDispose(); break; // case SWT.MouseDown: onMouseDown(event); break; // case SWT.MouseExit: onMouseExit(); break; // case SWT.MouseMove: onMouseMove(event); break; // case SWT.MouseUp: onMouseUp(event); break; // case SWT.MouseDoubleClick: onMouseDoubleClick(event); break; // case SWT.Paint: onPaint(event); break; case SWT.Resize: onResize(); break; } } }; int[] events = new int[] { SWT.Dispose, // SWT.MouseDown, // SWT.MouseExit, // SWT.MouseMove, // SWT.MouseUp, // SWT.MouseDoubleClick, // SWT.Paint, SWT.Resize }; for ( int i = 0; i < events.length; i++ ) { addListener( events[i], listener ); } } @Override void initState() { removeState( /* CANVAS | */ THEME_BACKGROUND ); } private static int checkStyle( int style ) { int result = style; result |= SWT.NO_FOCUS; // return (style | SWT.NO_REDRAW_RESIZE) & ~(SWT.V_SCROLL | SWT.H_SCROLL); return (result) & ~(SWT.V_SCROLL | SWT.H_SCROLL); } // void _setCursor (Cursor cursor) { // if (this.cursor != null) return; // super.setCursor (cursor); // } @Override protected void checkSubclass() { if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); } @Override public Point computeSize( int wHint, int hHint, boolean changed ) { checkWidget(); int width = 0, height = 0; wrapItems( (style & SWT.VERTICAL) != 0 ? hHint : wHint ); boolean flat = (style & SWT.FLAT) != 0; for ( int row = 0; row < items.length; row++ ) { int rowWidth = 0, rowHeight = 0; for ( int i = 0; i < items[row].length; i++ ) { CoolItem item = items[row][i]; rowWidth += item.preferredWidth; rowHeight = Math.max( rowHeight, item.preferredHeight ); } height += rowHeight; if ( !flat && row > 0 ) height += ROW_SPACING; width = Math.max( width, rowWidth ); } wrapItems( getWidth() ); if ( width == 0 ) width = DEFAULT_COOLBAR_WIDTH; if ( height == 0 ) height = DEFAULT_COOLBAR_HEIGHT; if ( wHint != SWT.DEFAULT ) width = wHint; if ( hHint != SWT.DEFAULT ) height = hHint; Rectangle trim = computeTrim( 0, 0, width, height ); return fixPoint( trim.width, trim.height ); } CoolItem getGrabbedItem( int x, int y ) { for ( int row = 0; row < items.length; row++ ) { for ( int i = 0; i < items[row].length; i++ ) { CoolItem item = items[row][i]; Rectangle bounds = item.internalGetBounds(); bounds.width = CoolItem.MINIMUM_WIDTH; if ( bounds.x > x ) break; if ( bounds.y > y ) return null; if ( bounds.contains( x, y ) ) { return item; } } } return null; } /** * Returns the item that is currently displayed at the given, zero-relative * index. Throws an exception if the index is out of range. * * @param index * the visual index of the item to return * @return the item at the given visual index * * @exception IllegalArgumentException * <ul> * <li>ERROR_INVALID_RANGE - if the index is not between 0 and * the number of elements in the list minus 1 (inclusive)</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> */ public CoolItem getItem( int index ) { checkWidget(); CoolItem item = null; int sIndex = index; if ( index < 0 ) error( SWT.ERROR_INVALID_RANGE ); for ( int row = 0; row < items.length && item == null; row++ ) { if ( items[row].length > sIndex ) { item = items[row][sIndex]; } else { sIndex -= items[row].length; } } if( item == null ) { error( SWT.ERROR_INVALID_RANGE ); } return item; } /** * Returns the number of items contained in the receiver. * * @return the number of items * * @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> */ public int getItemCount() { checkWidget(); return originalItems.length; } /** * Returns an array of <code>CoolItem</code>s in the order in which they * are currently being displayed. * <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 receiver's items in their current visual order * * @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> */ public CoolItem[] getItems() { checkWidget(); CoolItem[] result = new CoolItem[getItemCount()]; int offset = 0; for ( int row = 0; row < items.length; row++ ) { System.arraycopy( items[row], 0, result, offset, items[row].length ); offset += items[row].length; } return result; } Point findItem( CoolItem item ) { for ( int row = 0; row < items.length; row++ ) { for ( int i = 0; i < items[row].length; i++ ) { if ( items[row][i].equals( item ) ) return new Point( i, row ); } } return new Point( -1, -1 ); } void fixEvent( Event event ) { if ( (style & SWT.VERTICAL) != 0 ) { int tmp = event.x; event.x = event.y; event.y = tmp; } } Rectangle fixRectangle( int x, int y, int width, int height ) { if ( (style & SWT.VERTICAL) != 0 ) { return new Rectangle( y, x, height, width ); } return new Rectangle( x, y, width, height ); } Point fixPoint( int x, int y ) { if ( (style & SWT.VERTICAL) != 0 ) { return new Point( y, x ); } return new Point( x, y ); } /** * Searches the receiver's items in the order they are currently being * displayed, starting at the first item (index 0), until an item is found * that is equal to the argument, and returns the index of that item. If no * item is found, returns -1. * * @param item * the search item * @return the visual order index of the search item, or -1 if the item is not * found * * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item is disposed</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> */ @SuppressWarnings("all") public int indexOf( CoolItem item ) { checkWidget(); if ( item == null ) error( SWT.ERROR_NULL_ARGUMENT ); if ( item.isDisposed() ) error( SWT.ERROR_INVALID_ARGUMENT ); int answer = 0; for ( int row = 0; row < items.length; row++ ) { for ( int i = 0; i < items[row].length; i++ ) { if ( items[row][i].equals( item ) ) { return answer; } else { answer++; } } } return -1; } /** * Insert the item into the row. Adjust the x and width values appropriately. */ void insertItemIntoRow( CoolItem item, int rowIndex, int x_root ) { int barWidth = getWidth(); int rowY = items[rowIndex][0].internalGetBounds().y; int x = Math.max( 0, x_root - toDisplay( new Point( 0, 0 ) ).x ); /* Find the insertion index and add the item. */ int index; for ( index = 0; index < items[rowIndex].length; index++ ) { if ( x < items[rowIndex][index].internalGetBounds().x ) break; } if ( index == 0 ) { item.wrap = true; items[rowIndex][0].wrap = false; } int oldLength = items[rowIndex].length; CoolItem[] newRow = new CoolItem[oldLength + 1]; System.arraycopy( items[rowIndex], 0, newRow, 0, index ); newRow[index] = item; System.arraycopy( items[rowIndex], index, newRow, index + 1, oldLength - index ); items[rowIndex] = newRow; /* Adjust the width of the item to the left. */ if ( index > 0 ) { CoolItem left = items[rowIndex][index - 1]; Rectangle leftBounds = left.internalGetBounds(); int newWidth = x - leftBounds.x; if ( newWidth < left.internalGetMinimumWidth() ) { x += left.internalGetMinimumWidth() - newWidth; newWidth = left.internalGetMinimumWidth(); } left.setBounds( leftBounds.x, leftBounds.y, newWidth, leftBounds.height ); left.requestedWidth = newWidth; } /* Set the item's bounds. */ int width = 0, height = item.internalGetBounds().height; if ( index < items[rowIndex].length - 1 ) { CoolItem right = items[rowIndex][index + 1]; width = right.internalGetBounds().x - x; if ( width < right.internalGetMinimumWidth() ) { moveRight( right, right.internalGetMinimumWidth() - width ); width = right.internalGetBounds().x - x; } item.setBounds( x, rowY, width, height ); if ( width < item.internalGetMinimumWidth() ) moveLeft( item, item.internalGetMinimumWidth() - width ); } else { width = Math.max( item.internalGetMinimumWidth(), barWidth - x ); item.setBounds( x, rowY, width, height ); if ( x + width > barWidth ) moveLeft( item, x + width - barWidth ); } Rectangle bounds = item.internalGetBounds(); item.requestedWidth = bounds.width; // internalRedraw(bounds.x, bounds.y, item.internalGetMinimumWidth(), // bounds.height); } // void internalRedraw (int x, int y, int width, int height) { // if ((style & SWT.VERTICAL) != 0) { // redraw (y, x, height, width, false); // } else { // redraw (x, y, width, height, false); // } // } void createItem( CoolItem item, int index ) { int itemCount = getItemCount(), row = 0; if ( !(0 <= index && index <= itemCount) ) error( SWT.ERROR_INVALID_RANGE ); if ( items.length == 0 ) { items = new CoolItem[1][1]; items[0][0] = item; } else { int i = index; /* find the row to insert into */ if ( index < itemCount ) { while (i > items[row].length) { i -= items[row].length; row++; } } else { row = items.length - 1; i = items[row].length; } // Set the last item in the row to the preferred size // and add the new one just to it's right int lastIndex = items[row].length - 1; CoolItem lastItem = items[row][lastIndex]; if ( lastItem.ideal ) { Rectangle bounds = lastItem.internalGetBounds(); bounds.width = lastItem.preferredWidth; bounds.height = lastItem.preferredHeight; lastItem.requestedWidth = lastItem.preferredWidth; lastItem.setBounds( bounds.x, bounds.y, bounds.width, bounds.height ); } if ( i == 0 ) { item.wrap = true; items[row][0].wrap = false; } int oldLength = items[row].length; CoolItem[] newRow = new CoolItem[oldLength + 1]; System.arraycopy( items[row], 0, newRow, 0, i ); newRow[i] = item; System.arraycopy( items[row], i, newRow, i + 1, oldLength - i ); items[row] = newRow; } item.requestedWidth = CoolItem.MINIMUM_WIDTH; int length = originalItems.length; CoolItem[] newOriginals = new CoolItem[length + 1]; System.arraycopy( originalItems, 0, newOriginals, 0, index ); System.arraycopy( originalItems, index, newOriginals, index + 1, length - index ); newOriginals[index] = item; originalItems = newOriginals; layoutItems(); } void destroyItem( CoolItem item ) { if ( inDispose ) return; int row = findItem( item ).y; if ( row == -1 ) return; // Rectangle bounds = item.internalGetBounds(); removeItemFromRow( item, row, true ); int index = 0; while (index < originalItems.length) { if ( originalItems[index] == item ) break; index++; } int length = originalItems.length - 1; CoolItem[] newOriginals = new CoolItem[length]; System.arraycopy( originalItems, 0, newOriginals, 0, index ); System.arraycopy( originalItems, index + 1, newOriginals, index, length - index ); originalItems = newOriginals; // internalRedraw(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH, // bounds.height); relayout(); } void moveDown( CoolItem item, int x_root ) { int oldRowIndex = findItem( item ).y; boolean resize = false; if ( items[oldRowIndex].length == 1 ) { resize = true; /* If this is the only item in the bottom row, don't move it. */ if ( oldRowIndex == items.length - 1 ) return; } int newRowIndex = (items[oldRowIndex].length == 1) ? oldRowIndex : oldRowIndex + 1; removeItemFromRow( item, oldRowIndex, false ); // Rectangle old = item.internalGetBounds(); // internalRedraw(old.x, old.y, CoolItem.MINIMUM_WIDTH, old.height); if ( newRowIndex == items.length ) { /* Create a new bottom row for the item. */ CoolItem[][] newRows = new CoolItem[items.length + 1][]; System.arraycopy( items, 0, newRows, 0, items.length ); int row = items.length; newRows[row] = new CoolItem[1]; newRows[row][0] = item; items = newRows; resize = true; item.wrap = true; } else { insertItemIntoRow( item, newRowIndex, x_root ); } if ( resize ) { relayout(); } else { layoutItems(); } } void moveLeft( CoolItem item, int pixels ) { Point point = findItem( item ); int row = point.y; int index = point.x; if ( index == 0 ) return; Rectangle bounds = item.internalGetBounds(); int minSpaceOnLeft = 0; for ( int i = 0; i < index; i++ ) { minSpaceOnLeft += items[row][i].internalGetMinimumWidth(); } int x = Math.max( minSpaceOnLeft, bounds.x - pixels ); CoolItem left = items[row][index - 1]; Rectangle leftBounds = left.internalGetBounds(); if ( leftBounds.x + left.internalGetMinimumWidth() > x ) { int shift = leftBounds.x + left.internalGetMinimumWidth() - x; moveLeft( left, shift ); leftBounds = left.internalGetBounds(); } int leftWidth = Math.max( left.internalGetMinimumWidth(), leftBounds.width - pixels ); left.setBounds( leftBounds.x, leftBounds.y, leftWidth, leftBounds.height ); left.requestedWidth = leftWidth; int width = bounds.width + (bounds.x - x); item.setBounds( x, bounds.y, width, bounds.height ); item.requestedWidth = width; // int damagedWidth = bounds.x - x + CoolItem.MINIMUM_WIDTH; // if (damagedWidth > CoolItem.MINIMUM_WIDTH) { // internalRedraw(x, bounds.y, damagedWidth, bounds.height); // } } void moveRight( CoolItem item, int pixels ) { Point point = findItem( item ); int row = point.y; int index = point.x; if ( index == 0 ) return; Rectangle bounds = item.internalGetBounds(); int minSpaceOnRight = 0; for ( int i = index; i < items[row].length; i++ ) { minSpaceOnRight += items[row][i].internalGetMinimumWidth(); } int max = getWidth() - minSpaceOnRight; int x = Math.min( max, bounds.x + pixels ); int width = 0; if ( index + 1 == items[row].length ) { width = getWidth() - x; } else { CoolItem right = items[row][index + 1]; Rectangle rightBounds = right.internalGetBounds(); if ( x + item.internalGetMinimumWidth() > rightBounds.x ) { int shift = x + item.internalGetMinimumWidth() - rightBounds.x; moveRight( right, shift ); rightBounds = right.internalGetBounds(); } width = rightBounds.x - x; } item.setBounds( x, bounds.y, width, bounds.height ); item.requestedWidth = width; CoolItem left = items[row][index - 1]; Rectangle leftBounds = left.internalGetBounds(); int leftWidth = x - leftBounds.x; left.setBounds( leftBounds.x, leftBounds.y, leftWidth, leftBounds.height ); left.requestedWidth = leftWidth; // int damagedWidth = x - bounds.x + CoolItem.MINIMUM_WIDTH // + CoolItem.MARGIN_WIDTH; // if (x - bounds.x > 0) { // internalRedraw(bounds.x - CoolItem.MARGIN_WIDTH, bounds.y, damagedWidth, // bounds.height); // } } void moveUp( CoolItem item, int x_root ) { Point point = findItem( item ); int oldRowIndex = point.y; boolean resize = false; if ( items[oldRowIndex].length == 1 ) { resize = true; /* If this is the only item in the top row, don't move it. */ if ( oldRowIndex == 0 ) return; } removeItemFromRow( item, oldRowIndex, false ); // Rectangle old = item.internalGetBounds(); // internalRedraw(old.x, old.y, CoolItem.MINIMUM_WIDTH, old.height); int newRowIndex = Math.max( 0, oldRowIndex - 1 ); if ( oldRowIndex == 0 ) { /* Create a new top row for the item. */ CoolItem[][] newRows = new CoolItem[items.length + 1][]; System.arraycopy( items, 0, newRows, 1, items.length ); newRows[0] = new CoolItem[1]; newRows[0][0] = item; items = newRows; resize = true; item.wrap = true; } else { insertItemIntoRow( item, newRowIndex, x_root ); } if ( resize ) { relayout(); } else { layoutItems(); } } void onDispose() { /* * Usually when an item is disposed, destroyItem will change the size of the * items array and reset the bounds of all the remaining cool items. Since * the whole cool bar is being disposed, this is not necessary. For speed * the inDispose flag is used to skip over this part of the item dispose. */ if ( inDispose ) return; inDispose = true; // notifyListeners(SWT.Dispose, event); // event.type = SWT.None; for ( int i = 0; i < items.length; i++ ) { for ( int j = 0; j < items[i].length; j++ ) { items[i][j].dispose(); } } // hoverCursor.dispose(); // dragCursor.dispose(); // cursor = null; } // void onMouseDown(Event event) { // if (isLocked || event.button != 1) return; // fixEvent(event); // dragging = getGrabbedItem(event.x, event.y); // if (dragging != null) { // mouseXOffset = event.x; // itemXOffset = mouseXOffset - dragging.internalGetBounds().x; // _setCursor(dragCursor); // } // fixEvent(event); // } // void onMouseExit() { // if (dragging == null) _setCursor(null); // } // void onMouseMove(Event event) { // if (isLocked) return; // fixEvent(event); // CoolItem grabbed = getGrabbedItem(event.x, event.y); // if (dragging != null) { // int left_root = toDisplay(new Point(event.x, event.y)).x - itemXOffset; // Rectangle bounds = dragging.internalGetBounds(); // if (event.y < bounds.y) { // moveUp(dragging, left_root); // } else if (event.y > bounds.y + bounds.height){ // moveDown(dragging, left_root); // } else if (event.x < mouseXOffset) { // int distance = Math.min(mouseXOffset, bounds.x + itemXOffset) - event.x; // if (distance > 0) moveLeft(dragging, distance); // } else if (event.x > mouseXOffset) { // int distance = event.x - Math.max(mouseXOffset, bounds.x + itemXOffset); // if (distance > 0) moveRight(dragging, distance); // } // mouseXOffset = event.x; // } else { // if (grabbed != null) { // _setCursor(hoverCursor); // } else { // _setCursor(null); // } // } // fixEvent(event); // } // void onMouseUp(Event event) { // _setCursor(null); // dragging = null; // } // void onMouseDoubleClick(Event event) { // if (isLocked) return; // dragging = null; // fixEvent(event); // CoolItem target = getGrabbedItem(event.x, event.y); // if (target == null) { // _setCursor(null); // } else { // Point location = findItem(target); // int row = location.y; // int index = location.x; // if (items[row].length > 1) { // Rectangle bounds = target.internalGetBounds(); // int maxSize = getWidth (); // for (int i = 0; i < items[row].length; i++) { // if (i != index) { // maxSize -= items[row][i].internalGetMinimumWidth(); // } // } // if (bounds.width == maxSize) { // /* The item is at its maximum width. It should be resized to its minimum // width. */ // int distance = bounds.width - target.internalGetMinimumWidth(); // if (index + 1 < items[row].length) { // /* There is an item to the right. Maximize it. */ // CoolItem right = items[row][index + 1]; // moveLeft(right, distance); // } else { // /* There is no item to the right. Move the item all the way right. */ // moveRight(target, distance); // } // } else if (bounds.width < target.preferredWidth) { // /* The item is less than its preferredWidth. Resize to preferredWidth. */ // int distance = target.preferredWidth - bounds.width; // if (index + 1 < items[row].length) { // CoolItem right = items[row][index + 1]; // moveRight(right, distance); // distance = target.preferredWidth - target.internalGetBounds().width; // } // if (distance > 0) { // moveLeft(target, distance); // } // } else { // /* The item is at its minimum width. Maximize it. */ // for (int i = 0; i < items[row].length; i++) { // if (i != index) { // CoolItem item = items[row][i]; // item.requestedWidth = Math.max(item.internalGetMinimumWidth(), // CoolItem.MINIMUM_WIDTH); // } // } // target.requestedWidth = maxSize; // layoutItems(); // } // _setCursor(hoverCursor); // } // } // fixEvent(event); // } // void onPaint(Event event) { // GC gc = event.gc; // if (items.length == 0) return; // Color shadowColor = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); // Color highlightColor = // display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW); // boolean vertical = (style & SWT.VERTICAL) != 0; // boolean flat = (style & SWT.FLAT) != 0; // int stopX = getWidth(); // Rectangle rect; // Rectangle clipping = gc.getClipping(); // for (int row = 0; row < items.length; row++) { // Rectangle bounds = new Rectangle(0, 0, 0, 0); // for (int i = 0; i < items[row].length; i++) { // bounds = items[row][i].internalGetBounds(); // rect = fixRectangle(bounds.x, bounds.y, bounds.width, bounds.height); // if (!clipping.intersects(rect)) continue; // boolean nativeGripper = false; // // /* Draw gripper. */ // if (!isLocked) { // rect = fixRectangle(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH, // bounds.height); // if (!flat) nativeGripper = drawGripper(rect.x, rect.y, rect.width, // rect.height, vertical); // if (!nativeGripper) { // int grabberTrim = 2; // int grabberHeight = bounds.height - (2 * grabberTrim) - 1; // gc.setForeground(shadowColor); // rect = fixRectangle( // bounds.x + CoolItem.MARGIN_WIDTH, // bounds.y + grabberTrim, // 2, // grabberHeight); // gc.drawRectangle(rect); // gc.setForeground(highlightColor); // rect = fixRectangle( // bounds.x + CoolItem.MARGIN_WIDTH, // bounds.y + grabberTrim + 1, // bounds.x + CoolItem.MARGIN_WIDTH, // bounds.y + grabberTrim + grabberHeight - 1); // gc.drawLine(rect.x, rect.y, rect.width, rect.height); // rect = fixRectangle( // bounds.x + CoolItem.MARGIN_WIDTH, // bounds.y + grabberTrim, // bounds.x + CoolItem.MARGIN_WIDTH + 1, // bounds.y + grabberTrim); // gc.drawLine(rect.x, rect.y, rect.width, rect.height); // } // } // // /* Draw separator. */ // if (!flat && !nativeGripper && i != 0) { // gc.setForeground(shadowColor); // rect = fixRectangle(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height // - 1); // gc.drawLine(rect.x, rect.y, rect.width, rect.height); // gc.setForeground(highlightColor); // rect = fixRectangle(bounds.x + 1, bounds.y, bounds.x + 1, bounds.y + // bounds.height - 1); // gc.drawLine(rect.x, rect.y, rect.width, rect.height); // } // } // if (!flat && row + 1 < items.length) { // /* Draw row separator. */ // int separatorY = bounds.y + bounds.height; // gc.setForeground(shadowColor); // rect = fixRectangle(0, separatorY, stopX, separatorY); // gc.drawLine(rect.x, rect.y, rect.width, rect.height); // gc.setForeground(highlightColor); // rect = fixRectangle(0, separatorY + 1, stopX, separatorY + 1); // gc.drawLine(rect.x, rect.y, rect.width, rect.height); // } // } // } void onResize() { layoutItems(); } @Override void removeChild( Control control ) { super.removeChild( control ); CoolItem[] items = getItems(); for ( int i = 0; i < items.length; i++ ) { CoolItem item = items[i]; if ( item.control == control ) item.setControl( null ); } } /** * Remove the item from the row. Adjust the x and width values appropriately. */ void removeItemFromRow( CoolItem item, int rowIndex, boolean disposed ) { int index = findItem( item ).x; int newLength = items[rowIndex].length - 1; Rectangle itemBounds = item.internalGetBounds(); item.wrap = false; if ( newLength > 0 ) { CoolItem[] newRow = new CoolItem[newLength]; System.arraycopy( items[rowIndex], 0, newRow, 0, index ); System.arraycopy( items[rowIndex], index + 1, newRow, index, newRow.length - index ); items[rowIndex] = newRow; items[rowIndex][0].wrap = true; } else { CoolItem[][] newRows = new CoolItem[items.length - 1][]; System.arraycopy( items, 0, newRows, 0, rowIndex ); System.arraycopy( items, rowIndex + 1, newRows, rowIndex, newRows.length - rowIndex ); items = newRows; return; } if ( !disposed ) { if ( index == 0 ) { CoolItem first = items[rowIndex][0]; Rectangle bounds = first.internalGetBounds(); int width = bounds.x + bounds.width; first.setBounds( 0, bounds.y, width, bounds.height ); first.requestedWidth = width; // internalRedraw(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH, // bounds.height); } else { CoolItem previous = items[rowIndex][index - 1]; Rectangle bounds = previous.internalGetBounds(); int width = bounds.width + itemBounds.width; previous.setBounds( bounds.x, bounds.y, width, bounds.height ); previous.requestedWidth = width; } } } /** * Return the height of the bar after it has been properly laid out for the * given width. */ int layoutItems() { int y = 0, width; if ( (style & SWT.VERTICAL) != 0 ) { width = getClientArea().height; } else { width = getClientArea().width; } wrapItems( width ); int rowSpacing = (style & SWT.FLAT) != 0 ? 0 : ROW_SPACING; for ( int row = 0; row < items.length; row++ ) { int count = items[row].length; int x = 0; /* determine the height and the available width for the row */ int rowHeight = 0; int available = width; for ( int i = 0; i < count; i++ ) { CoolItem item = items[row][i]; rowHeight = Math.max(rowHeight, item.preferredHeight); available -= item.internalGetMinimumWidth(); } if ( row > 0 ) y += rowSpacing; /* lay the items out */ for ( int i = 0; i < count; i++ ) { CoolItem child = items[row][i]; int newWidth = available + child.internalGetMinimumWidth(); if ( i + 1 < count ) { newWidth = Math.min( newWidth, child.preferredWidth ); available -= (newWidth - child.internalGetMinimumWidth()); } Rectangle oldBounds = child.internalGetBounds(); Rectangle newBounds = new Rectangle( x, y, newWidth, rowHeight ); if ( !oldBounds.equals( newBounds ) ) { child.setBounds( newBounds.x, newBounds.y, newBounds.width, newBounds.height ); // Rectangle damage = new Rectangle(0, 0, 0, 0); /* Cases are in descending order from most area to redraw to least. */ // if (oldBounds.y != newBounds.y) { // damage = newBounds; // damage.add(oldBounds); // /* Redraw the row separator as well. */ // damage.y -= rowSpacing; // damage.height += 2 * rowSpacing; // } else if (oldBounds.height != newBounds.height) { // /* // * Draw from the bottom of the gripper to the bottom of the new // area. // * (Bottom of the gripper is -3 from the bottom of the item). // */ // damage.y = newBounds.y + Math.min(oldBounds.height, // newBounds.height) - 3; // damage.height = newBounds.y + newBounds.height + rowSpacing; // damage.x = oldBounds.x - CoolItem.MARGIN_WIDTH; // damage.width = oldBounds.width + CoolItem.MARGIN_WIDTH; // } else if (oldBounds.x != newBounds.x) { // /* Redraw only the difference between the separators. */ // damage.x = Math.min(oldBounds.x, newBounds.x); // damage.width = Math.abs(oldBounds.x - newBounds.x) + // CoolItem.MINIMUM_WIDTH; // damage.y = oldBounds.y; // damage.height = oldBounds.height; // } // internalRedraw(damage.x, damage.y, damage.width, damage.height); } x += newWidth; } y += rowHeight; } return y; } void relayout() { Point size = getSize(); int height = layoutItems(); if ((style & SWT.VERTICAL) != 0) { Rectangle trim = computeTrim (0, 0, height, 0); if (height != size.x) super.setSize(trim.width, size.y); } else { Rectangle trim = computeTrim (0, 0, 0, height); if (height != size.y) super.setSize(size.x, trim.height); } } /** * Returns an array of zero-relative ints that map the creation order of the * receiver's items to the order in which they are currently being displayed. * <p> * Specifically, the indices of the returned array represent the current * visual order of the items, and the contents of the array represent the * creation order of the items. * </p> * <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 current visual order of the receiver's items * * @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> */ public int[] getItemOrder() { checkWidget(); int count = getItemCount(); int[] indices = new int[count]; count = 0; for ( int i = 0; i < items.length; i++ ) { for ( int j = 0; j < items[i].length; j++ ) { CoolItem item = items[i][j]; int index = 0; while (index < originalItems.length) { if ( originalItems[index] == item ) break; index++; } if ( index == originalItems.length ) error( SWT.ERROR_CANNOT_GET_ITEM ); indices[count++] = index; } } return indices; } void setItemOrder( int[] itemOrder ) { if ( itemOrder == null ) error( SWT.ERROR_NULL_ARGUMENT ); int count = originalItems.length; if ( itemOrder.length != count ) error( SWT.ERROR_INVALID_ARGUMENT ); /* Ensure that itemOrder does not contain any duplicates. */ boolean[] set = new boolean[count]; for ( int i = 0; i < set.length; i++ ) set[i] = false; for ( int i = 0; i < itemOrder.length; i++ ) { if ( itemOrder[i] < 0 || itemOrder[i] >= count ) error( SWT.ERROR_INVALID_ARGUMENT ); if ( set[itemOrder[i]] ) error( SWT.ERROR_INVALID_ARGUMENT ); set[itemOrder[i]] = true; // CoolItem item = getItem( i ); // item.setOrder( itemOrder[i] ); } CoolItem[] row = new CoolItem[count]; for ( int i = 0; i < count; i++ ) { row[i] = originalItems[itemOrder[i]]; } items = new CoolItem[1][count]; items[0] = row; layoutItems(); } /** * Returns an array of points whose x and y coordinates describe the widths * and heights (respectively) of the items in the receiver in the order in * which they are currently being displayed. * * @return the receiver's item sizes in their current visual order * * @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> */ public Point[] getItemSizes() { checkWidget(); CoolItem[] items = getItems(); Point[] sizes = new Point[items.length]; for ( int i = 0; i < items.length; i++ ) { sizes[i] = items[i].getSize(); } return sizes; } void setItemSizes( Point[] sizes ) { if ( sizes == null ) error( SWT.ERROR_NULL_ARGUMENT ); CoolItem[] items = getItems(); if ( sizes.length != items.length ) error( SWT.ERROR_INVALID_ARGUMENT ); for ( int i = 0; i < items.length; i++ ) { items[i].setSize( sizes[i] ); } } /** * Returns whether or not the receiver is 'locked'. When a coolbar is locked, * its items cannot be repositioned. * * @return true if the coolbar is locked, false otherwise * * @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> */ public boolean getLocked() { checkWidget(); return isLocked; } int getWidth() { if ( (style & SWT.VERTICAL) != 0 ) return getSize().y; return getSize().x; } /** * Returns an array of ints that describe the zero-relative indices of any * item(s) in the receiver that will begin on a new row. The 0th visible item * always begins the first row, therefore it does not count as a wrap index. * * @return an array containing the receiver's wrap indices, or an empty array * if all items are in one row * * @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> */ public int[] getWrapIndices() { checkWidget(); if ( items.length <= 1 ) return new int[] {}; int[] wrapIndices = new int[items.length - 1]; int i = 0, nextWrap = items[0].length; for ( int row = 1; row < items.length; row++ ) { if ( items[row][0].wrap ) wrapIndices[i++] = nextWrap; nextWrap += items[row].length; } if ( i != wrapIndices.length ) { int[] tmp = new int[i]; System.arraycopy( wrapIndices, 0, tmp, 0, i ); return tmp; } return wrapIndices; } /** * Sets whether or not the receiver is 'locked'. When a coolbar is locked, its * items cannot be repositioned. * * @param locked * lock the coolbar if true, otherwise unlock the coolbar * * @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> */ public void setLocked( boolean locked ) { checkWidget(); if( isLocked != locked ) { redraw(); } isLocked = locked; } /** * Sets the indices of all item(s) in the receiver that will begin on a new * row. The indices are given in the order in which they are currently being * displayed. The 0th item always begins the first row, therefore it does not * count as a wrap index. If indices is null or empty, the items will be * placed on one line. * * @param indices * an array of wrap indices, or null * * @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> */ public void setWrapIndices( int[] indices ) { checkWidget(); int[] newIndices = indices; if ( newIndices == null ) newIndices = new int[0]; int count = originalItems.length; for ( int i = 0; i < newIndices.length; i++ ) { if ( newIndices[i] < 0 || newIndices[i] >= count ) { error( SWT.ERROR_INVALID_ARGUMENT ); } } for ( int i = 0; i < originalItems.length; i++ ) { originalItems[i].wrap = false; } for ( int i = 0; i < newIndices.length; i++ ) { int index = newIndices[i]; for ( int row = 0; row < items.length; row++ ) { if ( items[row].length > index ) { items[row][index].wrap = true; break; } index -= items[row].length; } } relayout(); } // public void setCursor (Cursor cursor) { // checkWidget (); // super.setCursor (this.cursor = cursor); // } /** * Sets the receiver's item order, wrap indices, and item sizes all at once. * This method is typically used to restore the displayed state of the * receiver to a previously stored state. * <p> * The item order is the order in which the items in the receiver should be * displayed, given in terms of the zero-relative ordering of when the items * were added. * </p> * <p> * The wrap indices are the indices of all item(s) in the receiver that will * begin on a new row. The indices are given in the order specified by the * item order. The 0th item always begins the first row, therefore it does not * count as a wrap index. If wrap indices is null or empty, the items will be * placed on one line. * </p> * <p> * The sizes are specified in an array of points whose x and y coordinates * describe the new widths and heights (respectively) of the receiver's items * in the order specified by the item order. * </p> * * @param itemOrder * an array of indices that describe the new order to display the * items in * @param wrapIndices * an array of wrap indices, or null * @param sizes * an array containing the new sizes for each of the receiver's items * in visual order * * @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> * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if item order or sizes is null</li> * <li>ERROR_INVALID_ARGUMENT - if item order or sizes is not * the same length as the number of items</li> * </ul> */ public void setItemLayout( int[] itemOrder, int[] wrapIndices, Point[] sizes ) { checkWidget(); setItemOrder( itemOrder ); setWrapIndices( wrapIndices ); setItemSizes( sizes ); relayout(); } void wrapItems( int maxWidth ) { int itemCount = originalItems.length; if ( itemCount < 2 ) return; CoolItem[] itemsVisual = new CoolItem[itemCount]; int start = 0; for ( int row = 0; row < items.length; row++ ) { System.arraycopy( items[row], 0, itemsVisual, start, items[row].length ); start += items[row].length; } CoolItem[][] newItems = new CoolItem[itemCount][]; int rowCount = 0, rowWidth = 0; start = 0; for ( int i = 0; i < itemCount; i++ ) { CoolItem item = itemsVisual[i]; int itemWidth = item.internalGetMinimumWidth(); if ( (i > 0 && item.wrap) || (maxWidth != SWT.DEFAULT && rowWidth + itemWidth > maxWidth) ) { if ( i == start ) { newItems[rowCount] = new CoolItem[1]; newItems[rowCount][0] = item; start = i + 1; rowWidth = 0; } else { int count = i - start; newItems[rowCount] = new CoolItem[count]; System.arraycopy( itemsVisual, start, newItems[rowCount], 0, count ); start = i; rowWidth = itemWidth; } rowCount++; } else { rowWidth += itemWidth; } } if ( start < itemCount ) { int count = itemCount - start; newItems[rowCount] = new CoolItem[count]; System.arraycopy( itemsVisual, start, newItems[rowCount], 0, count ); rowCount++; } if ( newItems.length != rowCount ) { CoolItem[][] tmp = new CoolItem[rowCount][]; System.arraycopy( newItems, 0, tmp, 0, rowCount ); items = tmp; } else { items = newItems; } } @Override @SuppressWarnings("unchecked") public <T> T getAdapter( Class<T> adapter ) { if ( adapter == IItemHolderAdapter.class ) { if( itemHolder == null ) { itemHolder = new CoolBarItemHolder(); } return ( T )itemHolder; } if ( adapter == ICoolBarAdapter.class ) { if( coolBarAdapter == null ) { coolBarAdapter = new CoolBarAdapter(); } return ( T )coolBarAdapter; } if ( adapter == WidgetLCA.class ) { return ( T )CoolBarLCA.INSTANCE; } return super.getAdapter( adapter ); } /////////////////// // Skinning support @Override void reskinChildren( int flags ) { CoolItem[] items = getItems(); if( items != null ) { for( int i = 0; i < items.length; i++ ) { CoolItem item = items[ i ]; if( item != null ) { item.reskin( flags ); } } } super.reskinChildren( flags ); } }