| /******************************************************************************* |
| * Copyright (c) 2003, 2004 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 |
| *******************************************************************************/ |
| package org.eclipse.jst.j2ee.internal.ejb.wizard; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.draw2d.IFigure; |
| import org.eclipse.draw2d.ToolbarLayout; |
| import org.eclipse.draw2d.geometry.Dimension; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| |
| |
| /** |
| * A ToolbarLayout-like layout for the palette. This layout is palette-specific and should not be |
| * used externally. This layout only works when vertically oriented. |
| * |
| * @author Pratik Shah |
| */ |
| public class PaletteToolbarLayout extends ToolbarLayout { |
| |
| private Rectangle[] sourceSizes; |
| private Rectangle[] destinationSizes; |
| private boolean scaling = false; |
| private DrawerAnimationController controller; |
| |
| /** |
| * Constructor |
| * |
| * @param controller |
| * Can be <code>null</code> if no animation is desired |
| */ |
| public PaletteToolbarLayout(DrawerAnimationController controller) { |
| super(); |
| this.controller = controller; |
| } |
| |
| private void captureDestinationSizes(IFigure parent) { |
| List children = parent.getChildren(); |
| destinationSizes = new Rectangle[children.size()]; |
| for (int i = 0; i < children.size(); i++) { |
| destinationSizes[i] = ((IFigure) children.get(i)).getBounds().getCopy(); |
| } |
| } |
| |
| private void captureSourceSizes(IFigure parent) { |
| List children = parent.getChildren(); |
| sourceSizes = new Rectangle[children.size()]; |
| for (int i = 0; i < children.size(); i++) { |
| sourceSizes[i] = ((IFigure) children.get(i)).getBounds().getCopy(); |
| } |
| } |
| |
| /** |
| * A figure is growing if it's an expanded drawer. |
| * |
| * @param child |
| * The figure that is to be marked as growing or non-growing |
| * @return <code>true</code> if the given child is considered growing |
| */ |
| protected boolean isChildGrowing(IFigure child) { |
| return child instanceof RelationshipDrawFigure && ((RelationshipDrawFigure) child).isExpanded(); |
| } |
| |
| private boolean isScaling() { |
| return scaling; |
| } |
| |
| /** |
| * @see org.eclipse.draw2d.ToolbarLayout#layout(org.eclipse.draw2d.IFigure) |
| */ |
| public void layout(IFigure parent) { |
| if (isScaling()) { |
| scaledLayout(parent); |
| setScaling(controller.isAnimationInProgress()); |
| } else { |
| if (controller != null && controller.isAnimationInProgress()) { |
| captureSourceSizes(parent); |
| normalLayout(parent); |
| captureDestinationSizes(parent); |
| setScaling(true); |
| } else { |
| normalLayout(parent); |
| } |
| } |
| } |
| |
| private void normalLayout(IFigure parent) { |
| List children = parent.getChildren(); |
| List childrenGrabbingVertical = new ArrayList(); |
| int numChildren = children.size(); |
| Rectangle clientArea = transposer.t(parent.getClientArea()); |
| int x = clientArea.x; |
| int y = clientArea.y; |
| int availableHeight = clientArea.height; |
| boolean stretching; |
| Dimension prefSizes[] = new Dimension[numChildren]; |
| Dimension minSizes[] = new Dimension[numChildren]; |
| int totalHeight = 0, totalMinHeight = 0, heightOfNonGrowingChildren = 0, heightPerChild = 0, excessHeight = 0; |
| |
| /* |
| * Determine hints. |
| */ |
| int wHint = isHorizontal() ? -1 : clientArea.width; |
| int hHint = isHorizontal() ? clientArea.width : -1; |
| |
| /* |
| * Store the preferred and minimum sizes of all figures. Determine which figures can be |
| * stretched/shrunk. |
| */ |
| for (int i = 0; i < numChildren; i++) { |
| IFigure child = (IFigure) children.get(i); |
| |
| prefSizes[i] = transposer.t(child.getPreferredSize(wHint, hHint)); |
| minSizes[i] = transposer.t(child.getMinimumSize(wHint, hHint)); |
| |
| totalHeight += prefSizes[i].height; |
| totalMinHeight += minSizes[i].height; |
| |
| if (isChildGrowing(child)) { |
| childrenGrabbingVertical.add(child); |
| } else { |
| heightOfNonGrowingChildren += prefSizes[i].height; |
| } |
| } |
| totalHeight += (numChildren - 1) * getSpacing(); |
| totalMinHeight += (numChildren - 1) * getSpacing(); |
| |
| /* |
| * This is the algorithm that determines which figures need to be compressed/stretched and |
| * by how much. |
| */ |
| stretching = totalHeight - Math.max(availableHeight, totalMinHeight) < 0; |
| // So long as there is at least one child that can be grown, figure out how much |
| // height should be given to each growing child. |
| if (!childrenGrabbingVertical.isEmpty()) { |
| // We only want the last child to stretch. So, we remove all but the last |
| // growing child. We add the preferred height of the children being marked |
| // non-growing to heightOfNonGrowingChildren. |
| if (stretching) { |
| for (int i = 0; i < childrenGrabbingVertical.size() - 1; i++) { |
| int index = children.indexOf(childrenGrabbingVertical.get(i)); |
| heightOfNonGrowingChildren += prefSizes[index].height; |
| } |
| Object last = childrenGrabbingVertical.get(childrenGrabbingVertical.size() - 1); |
| childrenGrabbingVertical.clear(); |
| childrenGrabbingVertical.add(last); |
| } |
| |
| boolean childrenDiscarded; |
| // spaceToConsume is the space height available on the palette that is to be |
| // shared by the growing children. |
| int spaceToConsume = availableHeight - heightOfNonGrowingChildren; |
| // heightPerChild is the height that each growing child is to be grown up to |
| heightPerChild = spaceToConsume / childrenGrabbingVertical.size(); |
| // excessHeight is the space leftover at the bottom of the palette after each |
| // growing child has been grown by heightPerChild. |
| excessHeight = spaceToConsume - (heightPerChild * childrenGrabbingVertical.size()); |
| do { |
| childrenDiscarded = false; |
| for (Iterator iter = childrenGrabbingVertical.iterator(); iter.hasNext();) { |
| IFigure childFig = (IFigure) iter.next(); |
| int i = childFig.getParent().getChildren().indexOf(childFig); |
| boolean discardChild = false; |
| if (stretching) { |
| // In case of stretching, if the child's height is greater than |
| // heightPerChild, mark that child as non-growing |
| discardChild = prefSizes[i].height > heightPerChild; |
| } else { |
| // In case of shrinking, if the child's height is smaller than |
| // heightPerChild, mark that child as non-growing |
| discardChild = prefSizes[i].height < heightPerChild; |
| } |
| if (discardChild) { |
| spaceToConsume -= prefSizes[i].height; |
| heightOfNonGrowingChildren += prefSizes[i].height; |
| childrenGrabbingVertical.remove(childFig); |
| childrenDiscarded = true; |
| heightPerChild = spaceToConsume / childrenGrabbingVertical.size(); |
| excessHeight = spaceToConsume - (heightPerChild * childrenGrabbingVertical.size()); |
| break; |
| } |
| } |
| } while (childrenDiscarded); |
| } |
| |
| /* |
| * Do the actual layout, i.e. set the bounds of all the figures. |
| */ |
| for (int i = 0; i < numChildren; i++) { |
| IFigure child = (IFigure) children.get(i); |
| |
| Rectangle newBounds = new Rectangle(x, y, prefSizes[i].width, prefSizes[i].height); |
| |
| /* |
| * Giving the excess available space to the last child causes one problem -- when it's |
| * about to start compressing, the scrollbar might appear, then disappear, and then |
| * re-appear as you keep making the palette shorter pixel by pixel. But this bug is rare |
| * (three compressible drawers have to be expanded for this bug to appear for one pixel, |
| * four for two pixels, five for three, and so on), and hence can be ignored. Also, for |
| * this bug to occur, the last child would have to be a compressible drawer (one whose |
| * min height is not the same as its pref height). |
| */ |
| if (childrenGrabbingVertical.contains(child)) { |
| // Set the height of growing children. If this is the last one, give it |
| // the excess height. |
| childrenGrabbingVertical.remove(child); |
| if (childrenGrabbingVertical.isEmpty()) { |
| newBounds.height = heightPerChild + excessHeight; |
| } else { |
| newBounds.height = heightPerChild; |
| } |
| } |
| |
| int minWidth = minSizes[i].width; |
| int width = Math.min(prefSizes[i].width, child.getMaximumSize().width); |
| if (getStretchMinorAxis()) |
| width = transposer.t(child.getMaximumSize()).width; |
| width = Math.max(minWidth, Math.min(clientArea.width, width)); |
| newBounds.width = width; |
| |
| int adjust = clientArea.width - width; |
| switch (getMinorAlignment()) { |
| case ALIGN_TOPLEFT : |
| adjust = 0; |
| break; |
| case ALIGN_CENTER : |
| adjust /= 2; |
| break; |
| case ALIGN_BOTTOMRIGHT : |
| break; |
| } |
| newBounds.x += adjust; |
| child.setBounds(transposer.t(newBounds)); |
| y += newBounds.height + getSpacing(); |
| } |
| } |
| |
| private void scaledLayout(IFigure parent) { |
| List children = parent.getChildren(); |
| float progress = controller.getAnimationProgress(); |
| |
| for (int i = 0; i < children.size(); i++) { |
| Rectangle rect1 = sourceSizes[i]; |
| Rectangle rect2 = destinationSizes[i]; |
| IFigure child = (IFigure) children.get(i); |
| |
| child.setBounds(new Rectangle(Math.round(progress * rect2.x + (1 - progress) * rect1.x), Math.round(progress * rect2.y + (1 - progress) * rect1.y), Math.round(progress * rect2.width + (1 - progress) * rect1.width), Math.round(progress * rect2.height + (1 - progress) * rect1.height))); |
| } |
| } |
| |
| private void setScaling(boolean newVal) { |
| scaling = newVal; |
| if (!scaling) { |
| sourceSizes = null; |
| destinationSizes = null; |
| } |
| } |
| |
| } |