# HG changeset patch # User Frank Benoit # Date 1200408318 -3600 # Node ID 8846d8f763632511af22cb973160d47f3ba9d371 # Parent a7afc89b7afec1e3ba852bceb98b9ea2d0254ce9 CoolBar and CoolItem diff -r a7afc89b7afe -r 8846d8f76363 dwt/widgets/Composite.d --- a/dwt/widgets/Composite.d Tue Jan 15 15:11:06 2008 +0100 +++ b/dwt/widgets/Composite.d Tue Jan 15 15:45:18 2008 +0100 @@ -218,6 +218,7 @@ return super.childStyle (); } +alias Scrollable.computeSize computeSize; override public Point computeSize (int wHint, int hHint, bool changed) { checkWidget (); if (wHint !is DWT.DEFAULT && wHint < 0) wHint = 0; diff -r a7afc89b7afe -r 8846d8f76363 dwt/widgets/CoolBar.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/widgets/CoolBar.d Tue Jan 15 15:45:18 2008 +0100 @@ -0,0 +1,1198 @@ +/******************************************************************************* + * Copyright (c) 2000, 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module dwt.widgets.CoolBar; + + + +import dwt.DWT; +import dwt.DWTException; +import dwt.graphics.Color; +import dwt.graphics.Cursor; +import dwt.graphics.GC; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Composite; +import dwt.widgets.CoolItem; +import dwt.widgets.Event; +import dwt.widgets.Control; +import dwt.widgets.Listener; + +/** + * Instances of this class provide an area for dynamically + * positioning the items they contain. + *

+ * The item children that may be added to instances of this class + * must be of type CoolItem. + *

+ * Note that although this class is a subclass of Composite, + * it does not make sense to add Control children to it, + * or set a layout on it. + *

+ *

+ *
Styles:
+ *
FLAT, HORIZONTAL, VERTICAL
+ *
Events:
+ *
(none)
+ *
+ *

+ * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. + *

+ * IMPORTANT: This class is not intended to be subclassed. + *

+ */ +public class CoolBar : Composite { + CoolItem[][] items; + CoolItem[] originalItems; + Cursor hoverCursor, dragCursor, cursor; + CoolItem dragging = null; + int mouseXOffset, itemXOffset; + bool isLocked = false; + bool inDispose = false; + static const int ROW_SPACING = 2; + static const int CLICK_DISTANCE = 3; + static const int DEFAULT_COOLBAR_WIDTH = 0; + static const int DEFAULT_COOLBAR_HEIGHT = 0; + +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + *

+ * The style value is either one of the style constants defined in + * class DWT which is applicable to instances of this + * class, or must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DWT style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + *

+ * + * @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 + * @exception DWTException + * + * @see DWT + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public this (Composite parent, int style) { + super (parent, checkStyle(style)); + if ((style & DWT.VERTICAL) !is 0) { + this.style |= DWT.VERTICAL; + hoverCursor = new Cursor(display, DWT.CURSOR_SIZENS); + } else { + this.style |= DWT.HORIZONTAL; + hoverCursor = new Cursor(display, DWT.CURSOR_SIZEWE); + } + dragCursor = new Cursor(display, DWT.CURSOR_SIZEALL); + Listener listener = new class () Listener { + public void handleEvent(Event event) { + switch (event.type) { + case DWT.Dispose: onDispose(event); break; + case DWT.MouseDown: onMouseDown(event); break; + case DWT.MouseExit: onMouseExit(); break; + case DWT.MouseMove: onMouseMove(event); break; + case DWT.MouseUp: onMouseUp(event); break; + case DWT.MouseDoubleClick: onMouseDoubleClick(event); break; + case DWT.Paint: onPaint(event); break; + case DWT.Resize: onResize(); break; + default: + } + } + }; + int[] events = [ + DWT.Dispose, + DWT.MouseDown, + DWT.MouseExit, + DWT.MouseMove, + DWT.MouseUp, + DWT.MouseDoubleClick, + DWT.Paint, + DWT.Resize + ]; + for (int i = 0; i < events.length; i++) { + addListener(events[i], listener); + } +} +private static int checkStyle (int style) { + style |= DWT.NO_FOCUS; + return (style | DWT.NO_REDRAW_RESIZE) & ~(DWT.V_SCROLL | DWT.H_SCROLL); +} +void _setCursor (Cursor cursor) { + if (this.cursor !is null) return; + super.setCursor (cursor); +} +protected void checkSubclass () { + if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS); +} + +alias Composite.computeSize computeSize; +public Point computeSize (int wHint, int hHint, bool changed) { + checkWidget(); + int width = 0, height = 0; + wrapItems((style & DWT.VERTICAL) !is 0 ? hHint : wHint); + bool flat = (style & DWT.FLAT) !is 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 is 0) width = DEFAULT_COOLBAR_WIDTH; + if (height is 0) height = DEFAULT_COOLBAR_HEIGHT; + if (wHint !is DWT.DEFAULT) width = wHint; + if (hHint !is DWT.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 + * @exception DWTException + */ +public CoolItem getItem (int index) { + checkWidget(); + if (index < 0) error (DWT.ERROR_INVALID_RANGE); + for (int row = 0; row < items.length; row++) { + if (items[row].length > index) { + return items[row][index]; + } else { + index -= items[row].length; + } + } + error (DWT.ERROR_INVALID_RANGE); + return null; +} +/** + * Returns the number of items contained in the receiver. + * + * @return the number of items + * + * @exception DWTException + */ +public int getItemCount () { + checkWidget(); + return originalItems.length; +} +/** + * Returns an array of CoolItems in the order + * in which they are currently being displayed. + *

+ * 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. + *

+ * + * @return the receiver's items in their current visual order + * + * @exception DWTException + */ +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] ==/*eq*/ item ) return new Point(i, row); + } + } + return new Point(-1, -1); +} +void fixEvent (Event event) { + if ((style & DWT.VERTICAL) !is 0) { + int tmp = event.x; + event.x = event.y; + event.y = tmp; + } +} +Rectangle fixRectangle (int x, int y, int width, int height) { + if ((style & DWT.VERTICAL) !is 0) { + return new Rectangle(y, x, height, width); + } + return new Rectangle(x, y, width, height); +} +Point fixPoint (int x, int y) { + if ((style & DWT.VERTICAL) !is 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 + * @exception DWTException + */ +public int indexOf (CoolItem item) { + checkWidget(); + if (item is null) error (DWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed()) error (DWT.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] ==/*eq*/ 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 is 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 & DWT.VERTICAL) !is 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 (DWT.ERROR_INVALID_RANGE); + if (items.length is 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 is 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 is -1) return; + Rectangle bounds = item.internalGetBounds(); + removeItemFromRow(item, row, true); + + int index = 0; + while (index < originalItems.length) { + if (originalItems [index] is 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; + bool resize = false; + if (items[oldRowIndex].length is 1) { + resize = true; + /* If this is the only item in the bottom row, don't move it. */ + if (oldRowIndex is items.length - 1) return; + } + int newRowIndex = (items[oldRowIndex].length is 1) ? oldRowIndex : oldRowIndex + 1; + removeItemFromRow(item, oldRowIndex, false); + Rectangle old = item.internalGetBounds(); + internalRedraw(old.x, old.y, CoolItem.MINIMUM_WIDTH, old.height); + if (newRowIndex is items.length) { + /* Create a new bottom row for the item. */ + CoolItem[][] newRows = new CoolItem[][](items.length + 1); + SimpleType!(CoolItem[]).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 is 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 is 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 is 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; + bool resize = false; + if (items[oldRowIndex].length is 1) { + resize = true; + /* If this is the only item in the top row, don't move it. */ + if (oldRowIndex is 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 is 0) { + /* Create a new top row for the item. */ + CoolItem[][] newRows = new CoolItem[][]( items.length + 1 ); + SimpleType!(CoolItem[]).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(Event event) { + /* + * 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(DWT.Dispose, event); + event.type = DWT.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 !is 1) return; + fixEvent(event); + dragging = getGrabbedItem(event.x, event.y); + if (dragging !is null) { + mouseXOffset = event.x; + itemXOffset = mouseXOffset - dragging.internalGetBounds().x; + _setCursor(dragCursor); + } + fixEvent(event); +} +void onMouseExit() { + if (dragging is null) _setCursor(null); +} +void onMouseMove(Event event) { + if (isLocked) return; + fixEvent(event); + CoolItem grabbed = getGrabbedItem(event.x, event.y); + if (dragging !is 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 !is 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 is 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 !is index) { + maxSize -= items[row][i].internalGetMinimumWidth(); + } + } + if (bounds.width is 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 !is 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 is 0) return; + Color shadowColor = display.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW); + Color highlightColor = display.getSystemColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW); + bool vertical = (style & DWT.VERTICAL) !is 0; + bool flat = (style & DWT.FLAT) !is 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; + bool 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 !is 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 (); +} +void removeControl (Control control) { + super.removeControl (control); + CoolItem [] items = getItems (); + for (int i=0; i 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 ); + SimpleType!(CoolItem[]).arraycopy(items, 0, newRows, 0, rowIndex); + SimpleType!(CoolItem[]).arraycopy(items, rowIndex + 1, newRows, rowIndex, newRows.length - rowIndex); + items = newRows; + return; + } + if (!disposed) { + if (index is 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&DWT.VERTICAL) !is 0) { + width = getClientArea().height; + } else { + width = getClientArea().width; + } + wrapItems(width); + int rowSpacing = (style & DWT.FLAT) !is 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.internalGetBounds().height); + 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.requestedWidth); + available -= (newWidth - child.internalGetMinimumWidth()); + } + Rectangle oldBounds = child.internalGetBounds(); + Rectangle newBounds = new Rectangle(x, y, newWidth, rowHeight); + if ( oldBounds !=/*eq*/ 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 !is newBounds.y) { + damage = newBounds; + damage.add(oldBounds); + /* Redraw the row separator as well. */ + damage.y -= rowSpacing; + damage.height += 2 * rowSpacing; + } else if (oldBounds.height !is 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 !is 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 & DWT.VERTICAL) !is 0) { + Rectangle trim = computeTrim (0, 0, height, 0); + if (height !is size.x) super.setSize(trim.width, size.y); + } else { + Rectangle trim = computeTrim (0, 0, 0, height); + if (height !is 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. + *

+ * 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. + *

+ * 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. + *

+ * + * @return the current visual order of the receiver's items + * + * @exception DWTException + */ +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= count) error (DWT.ERROR_INVALID_ARGUMENT); + if (set [itemOrder [i]]) error (DWT.ERROR_INVALID_ARGUMENT); + set [itemOrder [i]] = true; + } + + 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; +} +/** + * 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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +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 is null) error(DWT.ERROR_NULL_ARGUMENT); + CoolItem[] items = getItems(); + if (sizes.length !is items.length) error(DWT.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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @since 2.0 + */ +public bool getLocked () { + checkWidget (); + return isLocked; +} +int getWidth () { + if ((style & DWT.VERTICAL) !is 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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public int[] getWrapIndices () { + checkWidget(); + if (items.length <= 1) return null; + 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 !is 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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @since 2.0 + */ +public void setLocked (bool locked) { + checkWidget (); + if (isLocked !is 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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public void setWrapIndices (int[] indices) { + checkWidget(); + if (indices is null) indices = new int[0]; + int count = originalItems.length; + for (int i=0; i= count) { + error (DWT.ERROR_INVALID_ARGUMENT); + } + } + for (int i=0; i index) { + items[row][index].wrap = true; + break; + } else { + 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * + * @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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if item order or sizes is null
  • + *
  • ERROR_INVALID_ARGUMENT - if item order or sizes is not the same length as the number of items
  • + *
+ */ +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 !is DWT.DEFAULT && rowWidth + itemWidth > maxWidth)) { + if (i is 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 !is rowCount) { + CoolItem[][] tmp = new CoolItem[][](rowCount); + SimpleType!(CoolItem[]).arraycopy(newItems, 0, tmp, 0, rowCount); + items = tmp; + } else { + items = newItems; + } +} +} diff -r a7afc89b7afe -r 8846d8f76363 dwt/widgets/CoolItem.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/widgets/CoolItem.d Tue Jan 15 15:45:18 2008 +0100 @@ -0,0 +1,671 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + *******************************************************************************/ +module dwt.widgets.CoolItem; + + + +import dwt.DWT; +import dwt.DWTException; +import dwt.events.SelectionEvent; +import dwt.events.SelectionListener; +import dwt.graphics.Color; +import dwt.graphics.GC; +import dwt.graphics.Image; +import dwt.graphics.ImageData; +import dwt.graphics.PaletteData; +import dwt.graphics.Point; +import dwt.graphics.RGB; +import dwt.graphics.Rectangle; +import dwt.widgets.Item; +import dwt.widgets.Control; +import dwt.widgets.CoolBar; +import dwt.widgets.ToolBar; +import dwt.widgets.Event; +import dwt.widgets.TypedListener; +import dwt.widgets.ToolBar; +import dwt.widgets.ToolItem; +import dwt.widgets.Listener; + +/** + * Instances of this class are selectable user interface + * objects that represent the dynamically positionable + * areas of a CoolBar. + *
+ *
Styles:
+ *
DROP_DOWN
+ *
Events:
+ *
Selection
+ *
+ *

+ * IMPORTANT: This class is not intended to be subclassed. + *

+ */ +public class CoolItem : Item { + Control control; + CoolBar parent; + bool ideal; + int preferredWidth, preferredHeight, minimumWidth, minimumHeight, requestedWidth; + Rectangle itemBounds; + + static const int MARGIN_WIDTH = 4; + static const int GRABBER_WIDTH = 2; + static const int MINIMUM_WIDTH = (2 * MARGIN_WIDTH) + GRABBER_WIDTH; + + private int CHEVRON_HORIZONTAL_TRIM = -1; //platform dependent values + private int CHEVRON_VERTICAL_TRIM = -1; + private static const int CHEVRON_LEFT_MARGIN = 2; + private static const int CHEVRON_IMAGE_WIDTH = 8; //Width to draw the double arrow + + ToolBar chevron; + bool wrap; + Image arrowImage = null; + +/** + * Constructs a new instance of this class given its parent + * (which must be a CoolBar) and a style value + * describing its behavior and appearance. The item is added + * to the end of the items maintained by its parent. + *

+ * The style value is either one of the style constants defined in + * class DWT which is applicable to instances of this + * class, or must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DWT style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + *

+ * + * @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
    + *
  • ERROR_NULL_ARGUMENT - if the parent is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent
  • + *
  • ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass
  • + *
+ * + * @see DWT#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public this (CoolBar parent, int style) { + super(parent, style); + itemBounds = new Rectangle(0, 0, 0, 0); + this.parent = parent; + parent.createItem (this, parent.getItemCount()); + calculateChevronTrim (); +} +/** + * Constructs a new instance of this class given its parent + * (which must be a CoolBar), a style value + * describing its behavior and appearance, and the index + * at which to place it in the items maintained by its parent. + *

+ * The style value is either one of the style constants defined in + * class DWT which is applicable to instances of this + * class, or must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DWT style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + *

+ * + * @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 + * @param index the zero-relative index at which to store the receiver in its parent + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the parent is null
  • + *
  • ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)
  • + *
+ * @exception DWTException
    + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent
  • + *
  • ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass
  • + *
+ * + * @see DWT#DROP_DOWN + * @see Widget#checkSubclass + * @see Widget#getStyle + */ +public this (CoolBar parent, int style, int index) { + super(parent, style); + itemBounds = new Rectangle(0, 0, 0, 0); + this.parent = parent; + parent.createItem (this, index); + calculateChevronTrim (); +} +/** + * Adds the listener to the collection of listeners that will + * be notified when the control is selected by the user, by sending it one + * of the messages defined in the SelectionListener + * interface. + *

+ * If widgetSelected is called when the mouse is over + * the drop-down arrow (or 'chevron') portion of the cool item, + * the event object detail field contains the value DWT.ARROW, + * and the x and y fields in the event object represent the point at + * the bottom left of the chevron, where the menu should be popped up. + * widgetDefaultSelected is not called. + *

+ * + * @param listener the listener which should be notified when the control is selected by the user + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the listener is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + * + * @since 2.0 + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener is null) error (DWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (DWT.Selection,typedListener); + addListener (DWT.DefaultSelection,typedListener); +} +protected void checkSubclass () { + if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS); +} +/* + * Find the trim size of the Toolbar widget in the current platform. + */ +void calculateChevronTrim () { + ToolBar tb = new ToolBar (parent, DWT.FLAT); + ToolItem ti = new ToolItem (tb, DWT.PUSH); + Image image = new Image (display, 1, 1); + ti.setImage (image); + Point size = tb.computeSize (DWT.DEFAULT, DWT.DEFAULT); + size = parent.fixPoint(size.x, size.y); + CHEVRON_HORIZONTAL_TRIM = size.x - 1; + CHEVRON_VERTICAL_TRIM = size.y - 1; + tb.dispose (); + ti.dispose (); + image.dispose (); +} +/** + * Returns the preferred size of the receiver. + *

+ * The preferred size of a CoolItem is the size that + * it would best be displayed at. The width hint and height hint arguments + * allow the caller to ask the instance questions such as "Given a particular + * width, how high does it need to be to show all of the contents?" + * To indicate that the caller does not wish to constrain a particular + * dimension, the constant DWT.DEFAULT is passed for the hint. + *

+ * + * @param wHint the width hint (can be DWT.DEFAULT) + * @param hHint the height hint (can be DWT.DEFAULT) + * @return the preferred size + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see Layout + * @see #getBounds + * @see #getSize + * @see Control#getBorderWidth + * @see Scrollable#computeTrim + * @see Scrollable#getClientArea + */ +public Point computeSize (int wHint, int hHint) { + checkWidget(); + int width = wHint, height = hHint; + if (wHint is DWT.DEFAULT) width = 32; + if (hHint is DWT.DEFAULT) height = 32; + if ((parent.style & DWT.VERTICAL) !is 0) { + height += MINIMUM_WIDTH; + } else { + width += MINIMUM_WIDTH; + } + return new Point (width, height); +} +public void dispose () { + if (isDisposed()) return; + + /* + * Must call parent.destroyItem() before super.dispose(), since it needs to + * query the bounds to properly remove the item. + */ + parent.destroyItem(this); + super.dispose (); + parent = null; + control = null; + + /* + * Although the parent for the chevron is the CoolBar (CoolItem can not be the parent) + * it has to be disposed with the item + */ + if (chevron !is null && !chevron.isDisposed()) chevron.dispose(); + chevron = null; + if (arrowImage !is null && !arrowImage.isDisposed()) arrowImage.dispose(); + arrowImage = null; +} + +Image createArrowImage (int width, int height) { + Point point = parent.fixPoint(width, height); + width = point.x; + height = point.y; + Color foreground = parent.getForeground (); + Color black = display.getSystemColor (DWT.COLOR_BLACK); + Color background = parent.getBackground (); + + PaletteData palette = new PaletteData ( [foreground.getRGB(), background.getRGB(), black.getRGB() ] ); + ImageData imageData = new ImageData (width, height, 4, palette); + imageData.transparentPixel = 1; + Image image = new Image (display, imageData); + + GC gc = new GC (image); + gc.setBackground (background); + gc.fillRectangle (0, 0, width, height); + gc.setForeground (black); + + int startX = 0 ; + if ((parent.style & DWT.VERTICAL) !is 0) { + startX = width - CHEVRON_IMAGE_WIDTH; + } + int startY = height / 6; + int step = 2; + gc.drawLine (startX, startY, startX + step, startY + step); + gc.drawLine (startX, startY + (2 * step), startX + step, startY + step); + startX++; + gc.drawLine (startX, startY, startX + step, startY + step); + gc.drawLine (startX, startY + (2 * step), startX + step, startY + step); + startX += 3; + gc.drawLine (startX, startY, startX + step, startY + step); + gc.drawLine (startX, startY + (2 * step), startX + step, startY + step); + startX++; + gc.drawLine (startX, startY, startX + step, startY + step); + gc.drawLine (startX, startY + (2 * step), startX + step, startY + step); + gc.dispose (); + return image; +} +/** + * Returns a rectangle describing the receiver's size and location + * relative to its parent. + * + * @return the receiver's bounding rectangle + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public Rectangle getBounds () { + checkWidget(); + return parent.fixRectangle(itemBounds.x, itemBounds.y, itemBounds.width, itemBounds.height); +} +Rectangle internalGetBounds () { + return new Rectangle(itemBounds.x, itemBounds.y, itemBounds.width, itemBounds.height); +} +/** + * Returns the control that is associated with the receiver. + * + * @return the control that is contained by the receiver + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public Control getControl () { + checkWidget(); + return control; +} +/** + * Returns the minimum size that the cool item can + * be resized to using the cool item's gripper. + * + * @return a point containing the minimum width and height of the cool item, in pixels + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @since 2.0 + */ +public Point getMinimumSize () { + checkWidget(); + return parent.fixPoint(minimumWidth, minimumHeight); +} +/** + * Returns the receiver's parent, which must be a CoolBar. + * + * @return the receiver's parent + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public CoolBar getParent () { + checkWidget(); + return parent; +} +/** + * Returns a point describing the receiver's ideal size. + * The x coordinate of the result is the ideal width of the receiver. + * The y coordinate of the result is the ideal height of the receiver. + * + * @return the receiver's ideal size + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public Point getPreferredSize () { + checkWidget(); + return parent.fixPoint(preferredWidth, preferredHeight); +} +/** + * Returns a point describing the receiver's size. The + * x coordinate of the result is the width of the receiver. + * The y coordinate of the result is the height of the + * receiver. + * + * @return the receiver's size + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public Point getSize () { + checkWidget(); + return parent.fixPoint(itemBounds.width, itemBounds.height); +} +int internalGetMinimumWidth () { + int width = minimumWidth + MINIMUM_WIDTH; + if ((style & DWT.DROP_DOWN) !is 0 && width < preferredWidth) { + width += CHEVRON_IMAGE_WIDTH + CHEVRON_HORIZONTAL_TRIM + CHEVRON_LEFT_MARGIN; + } + return width; +} +/* + * Called when the chevron is selected. + */ +void onSelection (Event ev) { + Rectangle bounds = chevron.getBounds(); + Event event = new Event(); + event.detail = DWT.ARROW; + if ((parent.style & DWT.VERTICAL) !is 0) { + event.x = bounds.x + bounds.width; + event.y = bounds.y; + } else { + event.x = bounds.x; + event.y = bounds.y + bounds.height; + } + postEvent (DWT.Selection, event); +} +/** + * Removes the listener from the collection of listeners that + * will be notified when the control is selected by the user. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the listener is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see SelectionListener + * @see #addSelectionListener + * + * @since 2.0 + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener is null) error (DWT.ERROR_NULL_ARGUMENT); + if (eventTable is null) return; + eventTable.unhook (DWT.Selection, listener); + eventTable.unhook (DWT.DefaultSelection,listener); +} +void setBounds (int x, int y, int width, int height) { + itemBounds.x = x; + itemBounds.y = y; + itemBounds.width = width; + itemBounds.height = height; + if (control !is null) { + int controlWidth = width - MINIMUM_WIDTH; + if ((style & DWT.DROP_DOWN) !is 0 && width < preferredWidth) { + controlWidth -= CHEVRON_IMAGE_WIDTH + CHEVRON_HORIZONTAL_TRIM + CHEVRON_LEFT_MARGIN; + } + control.setBounds (parent.fixRectangle(x + MINIMUM_WIDTH, y, controlWidth, height)); + } + updateChevron(); +} +/** + * Sets the control that is associated with the receiver + * to the argument. + * + * @param control the new control that will be contained by the receiver + * + * @exception IllegalArgumentException
    + *
  • ERROR_INVALID_ARGUMENT - if the control has been disposed
  • + *
  • ERROR_INVALID_PARENT - if the control is not in the same widget tree
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public void setControl (Control control) { + checkWidget(); + if (control !is null) { + if (control.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT); + if (control.parent !is parent) error (DWT.ERROR_INVALID_PARENT); + } + this.control = control; + if (control !is null) { + int controlWidth = itemBounds.width - MINIMUM_WIDTH; + if ((style & DWT.DROP_DOWN) !is 0 && itemBounds.width < preferredWidth) { + controlWidth -= CHEVRON_IMAGE_WIDTH + CHEVRON_HORIZONTAL_TRIM + CHEVRON_LEFT_MARGIN; + } + control.setBounds (parent.fixRectangle(itemBounds.x + MINIMUM_WIDTH, itemBounds.y, controlWidth, itemBounds.height)); + } +} +/** + * Sets the minimum size that the cool item can be resized to + * using the cool item's gripper, to the point specified by the arguments. + * + * @param width the minimum width of the cool item, in pixels + * @param height the minimum height of the cool item, in pixels + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @since 2.0 + */ +public void setMinimumSize (int width, int height) { + checkWidget (); + Point point = parent.fixPoint(width, height); + minimumWidth = point.x; + minimumHeight = point.y; +} +/** + * Sets the minimum size that the cool item can be resized to + * using the cool item's gripper, to the point specified by the argument. + * + * @param size a point representing the minimum width and height of the cool item, in pixels + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the point is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @since 2.0 + */ +public void setMinimumSize (Point size) { + checkWidget (); + if (size is null) error(DWT.ERROR_NULL_ARGUMENT); + setMinimumSize(size.x, size.y); +} +/** + * Sets the receiver's ideal size to the point specified by the arguments. + * + * @param width the new ideal width for the receiver + * @param height the new ideal height for the receiver + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public void setPreferredSize (int width, int height) { + checkWidget(); + ideal = true; + Point point = parent.fixPoint(width, height); + preferredWidth = Math.max (point.x, MINIMUM_WIDTH); + preferredHeight = point.y; +} +/** + * Sets the receiver's ideal size to the point specified by the argument. + * + * @param size the new ideal size for the receiver + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the point is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public void setPreferredSize (Point size) { + checkWidget(); + if (size is null) error(DWT.ERROR_NULL_ARGUMENT); + setPreferredSize(size.x, size.y); +} +/** + * Sets the receiver's size to the point specified by the arguments. + *

+ * Note: Attempting to set the width or height of the + * receiver to a negative number will cause that + * value to be set to zero instead. + *

+ * + * @param width the new width for the receiver + * @param height the new height for the receiver + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public void setSize (int width, int height) { + checkWidget(); + Point point = parent.fixPoint(width, height); + width = Math.max(point.x, minimumWidth + MINIMUM_WIDTH); + height = point.y; + if (!ideal) { + preferredWidth = width; + preferredHeight = height; + } + itemBounds.width = requestedWidth = width; + itemBounds.height = height; + if (control !is null) { + int controlWidth = width - MINIMUM_WIDTH; + if ((style & DWT.DROP_DOWN) !is 0 && width < preferredWidth) { + controlWidth -= CHEVRON_IMAGE_WIDTH + CHEVRON_HORIZONTAL_TRIM + CHEVRON_LEFT_MARGIN; + } + control.setSize(parent.fixPoint(controlWidth, height)); + } + parent.relayout(); + updateChevron(); +} +/** + * Sets the receiver's size to the point specified by the argument. + *

+ * Note: Attempting to set the width or height of the + * receiver to a negative number will cause them to be + * set to zero instead. + *

+ * + * @param size the new size for the receiver + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the point is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public void setSize (Point size) { + checkWidget(); + if (size is null) error (DWT.ERROR_NULL_ARGUMENT); + setSize (size.x, size.y); +} +void updateChevron() { + if (control !is null) { + int width = itemBounds.width; + if ((style & DWT.DROP_DOWN) !is 0 && width < preferredWidth) { + if (chevron is null) { + chevron = new ToolBar (parent, DWT.FLAT | DWT.NO_FOCUS); + ToolItem toolItem = new ToolItem (chevron, DWT.PUSH); + toolItem.addListener (DWT.Selection, new class () Listener { + public void handleEvent (Event event) { + this.outer.onSelection (event); + } + }); + } + int controlHeight, currentImageHeight = 0; + if ((parent.style & DWT.VERTICAL) !is 0) { + controlHeight = control.getSize ().x; + if (arrowImage !is null) currentImageHeight = arrowImage.getBounds().width; + } else { + controlHeight = control.getSize ().y; + if (arrowImage !is null) currentImageHeight = arrowImage.getBounds().height; + } + int height = Math.min (controlHeight, itemBounds.height); + int imageHeight = Math.max(1, height - CHEVRON_VERTICAL_TRIM); + if (currentImageHeight !is imageHeight) { + Image image = createArrowImage (CHEVRON_IMAGE_WIDTH, imageHeight); + chevron.getItem (0).setImage (image); + if (arrowImage !is null) arrowImage.dispose (); + arrowImage = image; + } + chevron.setBackground (parent.getBackground()); + chevron.setBounds (parent.fixRectangle ( + itemBounds.x + width - CHEVRON_LEFT_MARGIN - CHEVRON_IMAGE_WIDTH - CHEVRON_HORIZONTAL_TRIM, + itemBounds.y, + CHEVRON_IMAGE_WIDTH + CHEVRON_HORIZONTAL_TRIM, + height)); + chevron.setVisible(true); + } else { + if (chevron !is null) { + chevron.setVisible(false); + } + } + } +} +} diff -r a7afc89b7afe -r 8846d8f76363 dwt/widgets/ToolBar.d --- a/dwt/widgets/ToolBar.d Tue Jan 15 15:11:06 2008 +0100 +++ b/dwt/widgets/ToolBar.d Tue Jan 15 15:45:18 2008 +0100 @@ -140,6 +140,7 @@ } } +alias Composite.computeSize computeSize; public Point computeSize (int wHint, int hHint, bool changed) { checkWidget (); if (wHint !is DWT.DEFAULT && wHint < 0) wHint = 0; @@ -427,7 +428,8 @@ } } -int setBounds (int x, int y, int width, int height, bool move, bool resize) { +alias Composite.setBounds setBounds; +override int setBounds (int x, int y, int width, int height, bool move, bool resize) { int result = super.setBounds (x, y, width, height, move, resize); if ((result & RESIZED) !is 0) relayout (); return result;