You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/03/16 17:16:58 UTC
svn commit: r754926 [33/38] - in /incubator/pivot/tags/v1.0: ./ charts-test/
charts-test/src/ charts-test/src/pivot/ charts-test/src/pivot/charts/
charts-test/src/pivot/charts/test/ charts/ charts/lib/ charts/src/
charts/src/pivot/ charts/src/pivot/cha...
Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraRollupSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraRollupSkin.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraRollupSkin.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraRollupSkin.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.RoundRectangle2D;
+
+import pivot.collections.Sequence;
+import pivot.util.Vote;
+import pivot.wtk.Button;
+import pivot.wtk.ButtonPressListener;
+import pivot.wtk.Component;
+import pivot.wtk.ComponentMouseButtonListener;
+import pivot.wtk.Container;
+import pivot.wtk.Cursor;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Mouse;
+import pivot.wtk.PushButton;
+import pivot.wtk.Rollup;
+import pivot.wtk.RollupListener;
+import pivot.wtk.Theme;
+import pivot.wtk.effects.Transition;
+import pivot.wtk.effects.TransitionListener;
+import pivot.wtk.effects.easing.Easing;
+import pivot.wtk.effects.easing.Quadratic;
+import pivot.wtk.media.Image;
+import pivot.wtk.skin.ButtonSkin;
+import pivot.wtk.skin.ContainerSkin;
+
+/**
+ * Rollup skin.
+ * <p>
+ * TODO Optimize this class by performing preferred size calculation in one
+ * pass.
+ *
+ * @author tvolkert
+ */
+public class TerraRollupSkin extends ContainerSkin
+ implements RollupListener, ButtonPressListener {
+ private class ExpansionTransition extends Transition {
+ private int height1;
+ private int height2;
+ private boolean reverse;
+
+ private int originalPreferredHeight;
+ private int height;
+
+ private Easing easing = new Quadratic();
+
+ public ExpansionTransition(int height1, int height2, boolean reverse, int duration, int rate) {
+ super(duration, rate, false);
+
+ this.height1 = height1;
+ this.height2 = height2;
+ this.reverse = reverse;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ @Override
+ public void start(TransitionListener transitionListener) {
+ Rollup rollup = (Rollup)getComponent();
+ originalPreferredHeight = rollup.isPreferredHeightSet() ?
+ rollup.getPreferredHeight() : -1;
+
+ super.start(transitionListener);
+ }
+
+ @Override
+ public void stop() {
+ Rollup rollup = (Rollup)getComponent();
+ rollup.setPreferredHeight(originalPreferredHeight);
+
+ super.stop();
+ }
+
+ @Override
+ protected void update() {
+ float percentComplete = getPercentComplete();
+
+ if (percentComplete < 1f) {
+ int elapsedTime = getElapsedTime();
+ int duration = getDuration();
+
+ height = (int)(height1 + (height2 - height1) * percentComplete);
+ if (reverse) {
+ height = (int)easing.easeIn(elapsedTime, height1, height2 - height1, duration);
+ } else {
+ height = (int)easing.easeOut(elapsedTime, height1, height2 - height1, duration);
+ }
+
+ Rollup rollup = (Rollup)getComponent();
+ rollup.setPreferredHeight(height);
+ }
+ }
+ }
+
+ protected class RollupButton extends PushButton {
+ public RollupButton() {
+ super(null);
+ setSkin(new RollupButtonSkin());
+ }
+ }
+
+ protected class RollupButtonSkin extends ButtonSkin {
+ @Override
+ public boolean isFocusable() {
+ return false;
+ }
+
+ public int getPreferredWidth(int height) {
+ RollupButton rollupButton = (RollupButton)getComponent();
+ Button.DataRenderer dataRenderer = rollupButton.getDataRenderer();
+ dataRenderer.render(rollupButton.getButtonData(), rollupButton, false);
+ return dataRenderer.getPreferredWidth(height);
+ }
+
+ public int getPreferredHeight(int width) {
+ RollupButton rollupButton = (RollupButton)getComponent();
+ Button.DataRenderer dataRenderer = rollupButton.getDataRenderer();
+ dataRenderer.render(rollupButton.getButtonData(), rollupButton, false);
+ return dataRenderer.getPreferredHeight(width);
+ }
+
+ public Dimensions getPreferredSize() {
+ RollupButton rollupButton = (RollupButton)getComponent();
+ Button.DataRenderer dataRenderer = rollupButton.getDataRenderer();
+ dataRenderer.render(rollupButton.getButtonData(), rollupButton, false);
+ Dimensions contentSize = dataRenderer.getPreferredSize();
+ return new Dimensions(contentSize.width, contentSize.height);
+ }
+
+ public void paint(Graphics2D graphics) {
+ RollupButton rollupButton = (RollupButton)getComponent();
+
+ // Paint the content
+ Button.DataRenderer dataRenderer = rollupButton.getDataRenderer();
+ dataRenderer.render(rollupButton.getButtonData(), rollupButton, false);
+
+ Dimensions contentSize = dataRenderer.getPreferredSize();
+ dataRenderer.setSize(contentSize.width, contentSize.height);
+ dataRenderer.paint(graphics);
+ }
+
+ @Override
+ public void mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+ PushButton pushButton = (PushButton)getComponent();
+ pushButton.press();
+ }
+ }
+
+ protected abstract class ButtonImage extends Image {
+ public int getWidth() {
+ return 7;
+ }
+
+ public int getHeight() {
+ return 7;
+ }
+ }
+
+ protected class ExpandImage extends ButtonImage {
+ public void paint(Graphics2D graphics) {
+ graphics.setStroke(new BasicStroke(0));
+ graphics.setPaint(buttonColor);
+
+ GeneralPath shape = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
+ shape.moveTo(0, 0);
+ shape.lineTo(6, 3);
+ shape.lineTo(0, 6);
+ shape.closePath();
+
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ graphics.draw(shape);
+ graphics.fill(shape);
+ }
+ }
+
+ protected class CollapseImage extends ButtonImage {
+ public void paint(Graphics2D graphics) {
+ graphics.setStroke(new BasicStroke(0));
+ graphics.setPaint(buttonColor);
+
+ GeneralPath shape = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
+ shape.moveTo(0, 0);
+ shape.lineTo(3, 6);
+ shape.lineTo(6, 0);
+ shape.closePath();
+
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ graphics.draw(shape);
+ graphics.fill(shape);
+ }
+ }
+
+ protected class BulletImage extends ButtonImage {
+ public void paint(Graphics2D graphics) {
+ graphics.setStroke(new BasicStroke(0));
+ graphics.setPaint(buttonColor);
+
+ RoundRectangle2D.Double shape = new RoundRectangle2D.Double(1, 1, 4, 4, 2, 2);
+
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ graphics.draw(shape);
+ graphics.fill(shape);
+ }
+ }
+
+ private class ToggleComponentMouseHandler
+ implements ComponentMouseButtonListener {
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ return false;
+ }
+
+ public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
+ return false;
+ }
+
+ public void mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+ Rollup rollup = (Rollup)getComponent();
+ rollup.setExpanded(!rollup.isExpanded());
+ }
+ }
+
+ private RollupButton rollupButton = null;
+ private Component toggleComponent = null;
+ private ToggleComponentMouseHandler toggleComponentMouseHandler =
+ new ToggleComponentMouseHandler();
+ private ExpandImage expandImage = new ExpandImage();
+ private CollapseImage collapseImage = new CollapseImage();
+ private BulletImage bulletImage = new BulletImage();
+
+ private ExpansionTransition expandTransition = null;
+ private ExpansionTransition collapseTransition = null;
+
+ private Color buttonColor;
+ private int spacing;
+ private int buffer;
+ private boolean justify;
+ private boolean firstChildToggles;
+
+ private static final int EXPANSION_DURATION = 250;
+ private static final int EXPANSION_RATE = 30;
+
+ public TerraRollupSkin() {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ buttonColor = theme.getColor(9);
+ spacing = 4;
+ buffer = 4;
+ justify = false;
+ firstChildToggles = true;
+ }
+
+ @Override
+ public void install(Component component) {
+ super.install(component);
+
+ Rollup rollup = (Rollup)component;
+ rollup.getRollupListeners().add(this);
+
+ updateToggleComponent();
+
+ rollupButton = new RollupButton();
+ updateRollupButton();
+ rollup.add(rollupButton);
+ rollupButton.getButtonPressListeners().add(this);
+ }
+
+ @Override
+ public void uninstall() {
+ Rollup rollup = (Rollup)getComponent();
+ rollup.getRollupListeners().remove(this);
+
+ rollupButton.getButtonPressListeners().remove(this);
+ rollup.remove(rollupButton);
+ rollupButton = null;
+
+ if (toggleComponent != null) {
+ toggleComponent.getComponentMouseButtonListeners().remove(toggleComponentMouseHandler);
+ toggleComponent = null;
+ }
+
+ super.uninstall();
+ }
+
+ @Override
+ public int getPreferredWidth(int height) {
+ Rollup rollup = (Rollup)getComponent();
+
+ int preferredWidth = 0;
+
+ // Preferred width is the max of our childrens' preferred widths, plus
+ // the button width, buffer, and padding. If we're collapsed, we only
+ // look at the first child.
+ for (int i = 0, n = rollup.getLength(); i < n; i++) {
+ Component component = rollup.get(i);
+
+ if (component == rollupButton) {
+ // Ignore "private" component
+ continue;
+ }
+
+ if (component.isDisplayable()) {
+ int componentPreferredWidth = component.getPreferredWidth(-1);
+ preferredWidth = Math.max(preferredWidth, componentPreferredWidth);
+ }
+
+ if (!rollup.isExpanded()) {
+ // If we're collapsed, we only look at the first child.
+ break;
+ }
+ }
+
+ preferredWidth += rollupButton.getPreferredWidth(-1) + buffer;
+
+ return preferredWidth;
+ }
+
+ @Override
+ public int getPreferredHeight(int width) {
+ Rollup rollup = (Rollup)getComponent();
+ return getPreferredHeight(width, rollup.isExpanded());
+ }
+
+ private int getPreferredHeight(int width, boolean expanded) {
+ Rollup rollup = (Rollup)getComponent();
+
+ // Preferred height is the sum of our childrens' preferred heights,
+ // plus spacing and padding.
+ Dimensions rollupButtonPreferredSize = rollupButton.getPreferredSize();
+
+ if (justify
+ && width != -1) {
+ width = Math.max(width - rollupButtonPreferredSize.width - buffer, 0);
+ } else {
+ width = -1;
+ }
+
+ int preferredHeight = 0;
+
+ int displayableComponentCount = 0;
+ for (int i = 0, n = rollup.getLength(); i < n; i++) {
+ Component component = rollup.get(i);
+
+ if (component == rollupButton) {
+ // Ignore "private" component
+ continue;
+ }
+
+ if (component.isDisplayable()) {
+ preferredHeight += component.getPreferredHeight(width);
+ displayableComponentCount++;
+ }
+
+ if (!expanded) {
+ // If we're collapsed, we only look at the first child.
+ break;
+ }
+ }
+
+ if (displayableComponentCount > 0) {
+ preferredHeight += (displayableComponentCount - 1) * spacing;
+ }
+
+ preferredHeight = Math.max(preferredHeight,
+ rollupButtonPreferredSize.height);
+
+ return preferredHeight;
+ }
+
+ public void layout() {
+ Rollup rollup = (Rollup)getComponent();
+ Dimensions rollupButtonSize = rollupButton.getPreferredSize();
+ rollupButton.setSize(rollupButtonSize);
+
+ int x = rollupButtonSize.width + buffer;
+ int y = 0;
+ int justifiedWidth = Math.max(getWidth() - rollupButtonSize.width - buffer, 0);
+
+ Component firstComponent = null;
+
+ for (int i = 0, n = rollup.getLength(); i < n; i++) {
+ Component component = rollup.get(i);
+
+ if (component == rollupButton) {
+ // Ignore "private" component
+ continue;
+ }
+
+ if (firstComponent == null) {
+ firstComponent = component;
+ }
+
+ if ((component == firstComponent
+ || rollup.isExpanded())
+ && component.isDisplayable()) {
+ // We lay this child out and make sure it's painted.
+ component.setVisible(true);
+
+ int componentWidth, componentHeight;
+ if (justify) {
+ componentWidth = justifiedWidth;
+ componentHeight = component.getPreferredHeight(componentWidth);
+ } else {
+ Dimensions componentPreferredSize = component.getPreferredSize();
+ componentWidth = componentPreferredSize.width;
+ componentHeight = componentPreferredSize.height;
+ }
+
+ component.setLocation(x, y);
+ component.setSize(componentWidth, componentHeight);
+
+ y += componentHeight + spacing;
+ } else {
+ // We make sure this child doesn't get painted. There's also
+ // no need to lay the child out.
+ component.setVisible(false);
+ }
+ }
+
+ int rollupButtonY = (firstComponent == null) ?
+ 0 : (firstComponent.getHeight() - rollupButtonSize.height) / 2 + 1;
+
+ rollupButton.setLocation(0, rollupButtonY);
+ }
+
+ public Color getButtonColor() {
+ return buttonColor;
+ }
+
+ public void setButtonColor(Color buttonColor) {
+ this.buttonColor = buttonColor;
+ repaintComponent();
+ }
+
+ public final void setButtonColor(String buttonColor) {
+ if (buttonColor == null) {
+ throw new IllegalArgumentException("buttonColor is null.");
+ }
+
+ setButtonColor(decodeColor(buttonColor));
+ }
+
+ public int getSpacing() {
+ return spacing;
+ }
+
+ public void setSpacing(int spacing) {
+ this.spacing = spacing;
+ invalidateComponent();
+ }
+
+ public int getBuffer() {
+ return buffer;
+ }
+
+ public void setBuffer(int buffer) {
+ this.buffer = buffer;
+ invalidateComponent();
+ }
+
+ public boolean getJustify() {
+ return justify;
+ }
+
+ public void setJustify(boolean justify) {
+ this.justify = justify;
+ invalidateComponent();
+ }
+
+ public boolean getFirstChildToggles() {
+ return firstChildToggles;
+ }
+
+ public void setFirstChildToggles(boolean firstChildToggles) {
+ this.firstChildToggles = firstChildToggles;
+ updateToggleComponent();
+ }
+
+ private void updateRollupButton() {
+ Rollup rollup = (Rollup)getComponent();
+
+ Image buttonData = null;
+ Cursor cursor = Cursor.HAND;
+
+ // Make sure to account for rollupButton
+ if (rollup.getLength() == 2) {
+ buttonData = bulletImage;
+ cursor = Cursor.DEFAULT;
+ } else if (rollup.isExpanded()) {
+ buttonData = collapseImage;
+ } else {
+ buttonData = expandImage;
+ }
+
+ rollupButton.setButtonData(buttonData);
+ rollupButton.setCursor(cursor);
+ }
+
+ private void updateToggleComponent() {
+ Rollup rollup = (Rollup)getComponent();
+ Component previousToggleComponent = toggleComponent;
+
+ toggleComponent = null;
+ if (firstChildToggles) {
+ for (int i = 0, n = rollup.getLength(); i < n; i++) {
+ Component child = rollup.get(i);
+ if (child != rollupButton) {
+ toggleComponent = child;
+ break;
+ }
+ }
+ }
+
+ if (toggleComponent != null
+ && rollup.getLength() > 2) {
+ // TODO Record original cursor
+ toggleComponent.setCursor(Cursor.HAND);
+ }
+
+ if (toggleComponent != previousToggleComponent) {
+ if (previousToggleComponent != null) {
+ // TODO Restore original cursor
+ previousToggleComponent.setCursor(Cursor.DEFAULT);
+
+ previousToggleComponent.getComponentMouseButtonListeners().remove(toggleComponentMouseHandler);
+ }
+
+ if (toggleComponent != null) {
+ toggleComponent.getComponentMouseButtonListeners().add(toggleComponentMouseHandler);
+ }
+ }
+ }
+
+ // Container events
+ @Override
+ public void componentInserted(Container container, int index) {
+ super.componentInserted(container, index);
+
+ updateRollupButton();
+ updateToggleComponent();
+ }
+
+ @Override
+ public void componentsRemoved(Container container, int index, Sequence<Component> components) {
+ super.componentsRemoved(container, index, components);
+
+ updateRollupButton();
+ updateToggleComponent();
+ }
+
+ // RollupListener methods
+
+ public Vote previewExpandedChange(final Rollup rollup) {
+ Vote vote = Vote.APPROVE;
+
+ if (rollup.getDisplay() != null) {
+ if (rollup.isExpanded()) {
+ // Start a collapse transition, return false, and set the
+ // expanded state when the transition is complete
+ if (collapseTransition == null) {
+ int duration = EXPANSION_DURATION;
+ int height1 = getHeight();
+
+ if (expandTransition != null) {
+ // Stop the expand transition
+ expandTransition.stop();
+
+ // Record its progress so we can reverse it at the right point
+ duration = expandTransition.getElapsedTime();
+ height1 = expandTransition.getHeight();
+
+ expandTransition = null;
+ }
+
+ if (duration > 0) {
+ int height2 = getPreferredHeight(-1, false);
+
+ collapseTransition = new ExpansionTransition(height1, height2,
+ true, duration, EXPANSION_RATE);
+ collapseTransition.start(new TransitionListener() {
+ public void transitionCompleted(Transition transition) {
+ rollup.setExpanded(false);
+ collapseTransition = null;
+ }
+ });
+
+ vote = Vote.DEFER;
+ }
+ } else {
+ vote = collapseTransition.isRunning() ? Vote.DEFER : Vote.APPROVE;
+ }
+ }
+ }
+
+ return vote;
+ }
+
+ public void expandedChangeVetoed(Rollup rollup, Vote reason) {
+ if (reason == Vote.DENY
+ && collapseTransition != null) {
+ collapseTransition.stop();
+ collapseTransition = null;
+ }
+ }
+
+ public void expandedChanged(Rollup rollup) {
+ updateRollupButton();
+ invalidateComponent();
+
+ if (rollup.getDisplay() != null) {
+ if (rollup.isExpanded()) {
+ // Start an expansion transition
+ int height1 = getHeight();
+ int height2 = getPreferredHeight(-1, true);
+
+ expandTransition = new ExpansionTransition(height1, height2,
+ false, EXPANSION_DURATION, EXPANSION_RATE);
+ expandTransition.start(new TransitionListener() {
+ public void transitionCompleted(Transition transition) {
+ expandTransition = null;
+ }
+ });
+ }
+ }
+ }
+
+ // ButtonPressListener methods
+
+ public void buttonPressed(Button button) {
+ Rollup rollup = (Rollup)getComponent();
+ rollup.setExpanded(!rollup.isExpanded());
+ }
+}
Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollBarSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollBarSkin.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollBarSkin.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollBarSkin.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.GeneralPath;
+
+import pivot.wtk.ApplicationContext;
+import pivot.wtk.Component;
+import pivot.wtk.ComponentMouseListener;
+import pivot.wtk.ComponentMouseButtonListener;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Display;
+import pivot.wtk.Mouse;
+import pivot.wtk.Orientation;
+import pivot.wtk.Point;
+import pivot.wtk.ScrollBar;
+import pivot.wtk.ScrollBarListener;
+import pivot.wtk.ScrollBarValueListener;
+import pivot.wtk.Theme;
+import pivot.wtk.media.Image;
+import pivot.wtk.skin.ComponentSkin;
+import pivot.wtk.skin.ContainerSkin;
+
+/**
+ * Scroll bar skin.
+ *
+ * @author tvolkert
+ */
+public class TerraScrollBarSkin extends ContainerSkin
+ implements ScrollBarListener, ScrollBarValueListener {
+
+ /**
+ * The types of scroll that are supported.
+ *
+ * @author tvolkert
+ */
+ protected enum IncrementType {
+ UNIT,
+ BLOCK;
+ }
+
+ /**
+ * Encapsulates the code needed to perform timer-controlled scrolling. This
+ * class is used by <tt>TerraScrollBarSkin</tt> (automatic block increment
+ * scrolling) and <tt>ScrollButtonSkin</tt> (automatic unit increment
+ * scrolling).
+ *
+ * @author tvolkert
+ */
+ protected class AutomaticScroller {
+ public int direction;
+ public IncrementType incrementType;
+ public int stopValue;
+
+ private int timeoutID = -1;
+ private int intervalID = -1;
+
+ /**
+ * Starts scrolling the specified scroll bar with no stop value.
+ *
+ * @param direction
+ * <tt>1</tt> to adjust the scroll bar's value larger; <tt>-1</tt> to
+ * adjust it smaller
+ *
+ * @param incrementType
+ * Determines whether we'll use the scroll bar's unit increment or the
+ * block increment when scrolling
+ *
+ * @exception IllegalStateException
+ * If automatic scrolling of any scroll bar is already in progress.
+ * Only one scroll bar may be automatically scrolled at one time
+ */
+ public void start(int direction, IncrementType incrementType) {
+ start(direction, incrementType, -1);
+ }
+
+ /**
+ * Starts scrolling the specified scroll bar, stopping the scroll when
+ * the specified value has been reached.
+ *
+ * @param direction
+ * <tt>1</tt> to adjust the scroll bar's value larger; <tt>-1</tt> to
+ * adjust it smaller
+ *
+ * @param incrementType
+ * Determines whether we'll use the scroll bar's unit increment or the
+ * block increment when scrolling
+ *
+ * @param stopValue
+ * The value which, once reached, will stop the automatic scrolling.
+ * Use <tt>-1</tt> to specify no stop value
+ *
+ * @exception IllegalStateException
+ * If automatic scrolling of any scroll bar is already in progress.
+ * Only one scroll bar may be automatically scrolled at one time
+ */
+ public void start(int direction, IncrementType incrementType, int stopValue) {
+ if (timeoutID != -1
+ || intervalID != -1) {
+ throw new IllegalStateException("Already running");
+ }
+
+ this.direction = direction;
+ this.incrementType = incrementType;
+ this.stopValue = stopValue;
+
+ // Wait a timeout period, then begin repidly scrolling
+ timeoutID = ApplicationContext.setTimeout(new Runnable() {
+ public void run() {
+ intervalID = ApplicationContext.setInterval(new Runnable() {
+ public void run() {
+ scroll();
+ }
+ }, 30);
+
+ timeoutID = -1;
+ }
+ }, 400);
+
+ // We initially scroll once to register that we've started
+ scroll();
+ }
+
+ /**
+ * Stops any automatic scrolling in progress.
+ */
+ public void stop() {
+ if (timeoutID != -1) {
+ ApplicationContext.clearTimeout(timeoutID);
+ timeoutID = -1;
+ }
+
+ if (intervalID != -1) {
+ ApplicationContext.clearInterval(intervalID);
+ intervalID = -1;
+ }
+ }
+
+ private void scroll() {
+ ScrollBar scrollBar = (ScrollBar)TerraScrollBarSkin.this.getComponent();
+
+ int rangeStart = scrollBar.getRangeStart();
+ int rangeEnd = scrollBar.getRangeEnd();
+ int extent = scrollBar.getExtent();
+ int value = scrollBar.getValue();
+
+ int adjustment;
+
+ if (incrementType == IncrementType.UNIT) {
+ adjustment = direction * scrollBar.getUnitIncrement();
+ } else {
+ adjustment = direction * scrollBar.getBlockIncrement();
+ }
+
+ if (adjustment < 0) {
+ int newValue = Math.max(value + adjustment, rangeStart);
+ scrollBar.setValue(newValue);
+
+ if (stopValue != -1
+ && newValue < stopValue) {
+ // We've reached the explicit stop value
+ stop();
+ }
+
+ if (newValue == rangeStart) {
+ // We implicit stop at the minimum scroll bar value
+ stop();
+ }
+ } else {
+ int newValue = Math.min(value + adjustment, rangeEnd - extent);
+ scrollBar.setValue(newValue);
+
+ if (stopValue != -1
+ && newValue > stopValue) {
+ // We've reached the explicit stop value
+ stop();
+ }
+
+ if (newValue == rangeEnd - extent) {
+ // We implicitly stop at the maximum scroll bar value
+ stop();
+ }
+ }
+ }
+ }
+
+ /**
+ * Scroll bar scroll button component.
+ *
+ * @author tvolkert
+ */
+ protected class ScrollButton extends Component {
+ private int direction;
+ private ScrollButtonImage buttonImage;
+
+ public ScrollButton(int direction, ScrollButtonImage buttonImage) {
+ this.direction = direction;
+ this.buttonImage = buttonImage;
+ setSkin(new ScrollButtonSkin());
+ }
+
+ public int getDirection() {
+ return direction;
+ }
+
+ public ScrollButtonImage getButtonImage() {
+ return buttonImage;
+ }
+ }
+
+ /**
+ * Scroll bar scroll button component skin.
+ *
+ * @author tvolkert
+ */
+ protected class ScrollButtonSkin extends ComponentSkin {
+ private boolean highlighted = false;
+ private boolean pressed = false;
+
+ @Override
+ public boolean isFocusable() {
+ return false;
+ }
+
+ public int getPreferredWidth(int height) {
+ return 15;
+ }
+
+ public int getPreferredHeight(int width) {
+ return 15;
+ }
+
+ public void layout() {
+ // No-op
+ }
+
+ public void paint(Graphics2D graphics) {
+ // Apply scroll bar styles to the button
+ ScrollButton scrollButton = (ScrollButton)getComponent();
+ ScrollBar scrollBar = (ScrollBar)TerraScrollBarSkin.this.getComponent();
+
+ int width = getWidth();
+ int height = getHeight();
+
+ Color backgroundColor;
+ if (scrollButton.isEnabled()) {
+ if (pressed) {
+ backgroundColor = scrollButtonPressedBackgroundColor;
+ } else if (highlighted) {
+ backgroundColor = scrollButtonHighlightedBackgroundColor;
+ } else {
+ backgroundColor = scrollButtonBackgroundColor;
+ }
+ } else {
+ backgroundColor = scrollButtonDisabledBackgroundColor;
+ }
+
+ // Paint the background
+ graphics.setPaint(backgroundColor);
+ graphics.fillRect(1, 1, width - 2, height - 2);
+
+ // Paint the border
+ graphics.setPaint(borderColor);
+ graphics.setStroke(new BasicStroke());
+ graphics.drawRect(0, 0, width - 1, height - 1);
+
+ // Size the image to be proportional to our size
+ int buttonImageWidth, buttonImageHeight;
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ buttonImageWidth = (int)((float)width / 3.0f);
+ buttonImageHeight = (int)Math.floor((float)height / 2.0f);
+ } else {
+ buttonImageWidth = (int)Math.floor((float)width / 2.0f);
+ buttonImageHeight = (int)((float)height / 3.0f);
+ }
+ ScrollButtonImage buttonImage = scrollButton.getButtonImage();
+ buttonImage.setSize(buttonImageWidth, buttonImageHeight);
+
+ // Paint the image
+ Graphics2D imageGraphics = (Graphics2D)graphics.create();
+ int buttonImageX = (width - buttonImageWidth) / 2;
+ int buttonImageY = (height - buttonImageHeight) / 2;
+ imageGraphics.translate(buttonImageX, buttonImageY);
+ imageGraphics.clipRect(0, 0, buttonImageWidth, buttonImageHeight);
+ buttonImage.paint(imageGraphics);
+ imageGraphics.dispose();
+ }
+
+ @Override
+ public void enabledChanged(Component component) {
+ super.enabledChanged(component);
+
+ automaticScroller.stop();
+
+ pressed = false;
+ highlighted = false;
+ repaintComponent();
+ }
+
+ @Override
+ public void mouseOver(Component component) {
+ super.mouseOver(component);
+
+ highlighted = true;
+ repaintComponent();
+ }
+
+ @Override
+ public void mouseOut(Component component) {
+ super.mouseOut(component);
+
+ automaticScroller.stop();
+
+ pressed = false;
+ highlighted = false;
+ repaintComponent();
+ }
+
+ @Override
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseDown(component, button, x, y);
+
+ if (button == Mouse.Button.LEFT) {
+ ScrollButton scrollButton = (ScrollButton)getComponent();
+
+ // Start the automatic scroller. It'll be stopped when we
+ // mouse up or mouse out
+ automaticScroller.start(scrollButton.getDirection(),
+ IncrementType.UNIT);
+
+ pressed = true;
+ repaintComponent();
+
+ consumed = true;
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseUp(component, button, x, y);
+
+ if (button == Mouse.Button.LEFT) {
+ automaticScroller.stop();
+
+ pressed = false;
+ repaintComponent();
+ }
+
+ return consumed;
+ }
+ }
+
+ protected abstract class ScrollButtonImage extends Image {
+ private int width = 0;
+ private int height = 0;
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setSize(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+ }
+
+ protected class ScrollUpImage extends ScrollButtonImage {
+ public void paint(Graphics2D graphics) {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int width = getWidth();
+ int height = getHeight();
+
+ graphics.setPaint(scrollButtonImageColor);
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ GeneralPath arrow = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ arrow.moveTo((float)width + 0.5f, 0);
+ arrow.lineTo(0, (float)height / 2.0f);
+ arrow.lineTo((float)width + 0.5f, height);
+ } else {
+ arrow.moveTo(0, (float)height + 0.5f);
+ arrow.lineTo((float)width / 2.0f, 0);
+ arrow.lineTo(width, (float)height + 0.5f);
+ }
+
+ arrow.closePath();
+ // TODO Use Graphics#fillPolygon() as optimization?
+ graphics.fill(arrow);
+ }
+ }
+
+ protected class ScrollDownImage extends ScrollButtonImage {
+ public void paint(Graphics2D graphics) {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int width = getWidth();
+ int height = getHeight();
+
+ graphics.setPaint(scrollButtonImageColor);
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ GeneralPath arrow = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ arrow.moveTo(0, 0);
+ arrow.lineTo((float)width + 0.5f, (float)height / 2.0f);
+ arrow.lineTo(0, height);
+ } else {
+ arrow.moveTo(0, 0);
+ arrow.lineTo((float)width / 2.0f, (float)height + 0.5f);
+ arrow.lineTo(width, 0);
+ }
+
+ arrow.closePath();
+ // TODO Use Graphics#fillPolygon() as optimization?
+ graphics.fill(arrow);
+ }
+ }
+
+ /**
+ * Scroll bar scroll handle component.
+ *
+ * @author tvolkert
+ */
+ protected class ScrollHandle extends Component {
+ public ScrollHandle() {
+ setSkin(new ScrollHandleSkin());
+ }
+ }
+
+ /**
+ * Scroll bar scroll handle component skin.
+ *
+ * @author tvolkert
+ */
+ protected class ScrollHandleSkin extends ComponentSkin {
+ private class DisplayMouseHandler
+ implements ComponentMouseButtonListener {
+ public boolean mouseDown(Component component, Mouse.Button button,
+ int x, int y) {
+ return false;
+ }
+
+ public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
+ if (button == Mouse.Button.LEFT) {
+ assert (component instanceof Display);
+ component.getComponentMouseButtonListeners().remove(this);
+
+ highlighted = false;
+ repaintComponent();
+ }
+
+ return false;
+ }
+
+ public void mouseClick(Component component, Mouse.Button button,
+ int x, int y, int count) {
+ // No-op
+ }
+ }
+
+ private DisplayMouseHandler displayMouseHandler = new DisplayMouseHandler();
+
+ private boolean highlighted = false;
+
+ @Override
+ public boolean isFocusable() {
+ return false;
+ }
+
+ public int getPreferredWidth(int height) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getPreferredHeight(int width) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Dimensions getPreferredSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void layout() {
+ // No-op
+ }
+
+ public void paint(Graphics2D graphics) {
+ ScrollBar scrollBar = (ScrollBar)TerraScrollBarSkin.this.getComponent();
+
+ int width = getWidth();
+ int height = getHeight();
+
+ // Paint the background
+ Color backgroundColor = highlighted ?
+ scrollButtonHighlightedBackgroundColor :
+ scrollButtonBackgroundColor;
+ graphics.setPaint(backgroundColor);
+ graphics.fillRect(1, 1, width - 2, height - 2);
+
+ // Paint the border
+ graphics.setPaint(borderColor);
+ graphics.setStroke(new BasicStroke());
+ graphics.drawRect(0, 0, width - 1, height - 1);
+
+ // Paint the hash marks
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ int middle = width / 2;
+ graphics.setPaint(TerraTheme.darken(backgroundColor));
+ graphics.drawLine(middle - 3, 4, middle - 3, height - 5);
+ graphics.drawLine(middle, 4, middle, height - 5);
+ graphics.drawLine(middle + 3, 4, middle + 3, height - 5);
+ graphics.setPaint(Color.WHITE);
+ graphics.drawLine(middle - 2, 4, middle - 2, height - 5);
+ graphics.drawLine(middle + 1, 4, middle + 1, height - 5);
+ graphics.drawLine(middle + 4, 4, middle + 4, height - 5);
+ } else {
+ int middle = height / 2;
+ graphics.setPaint(TerraTheme.darken(backgroundColor));
+ graphics.drawLine(4, middle - 3, width - 5, middle - 3);
+ graphics.drawLine(4, middle, width - 5, middle);
+ graphics.drawLine(4, middle + 3, width - 5, middle + 3);
+ graphics.setPaint(Color.WHITE);
+ graphics.drawLine(4, middle - 2, width - 5, middle - 2);
+ graphics.drawLine(4, middle + 1, width - 5, middle + 1);
+ graphics.drawLine(4, middle + 4, width - 5, middle + 4);
+ }
+ }
+
+ @Override
+ public void mouseOver(Component component) {
+ super.mouseOver(component);
+
+ if (highlighted) {
+ // If the handle is already highlighted when the mouse enters
+ // it, it means that the handle is "grabbed", meaning that we
+ // have registered our display mouse handler. Unregister it
+ // here so as to not register multiple times as we move our
+ // mouse in and out of the handle
+ Display display = component.getDisplay();
+ display.getComponentMouseButtonListeners().remove(displayMouseHandler);
+ } else {
+ // The handle is highlighted as long as the mouse is over it or
+ // we're dragging it
+ highlighted = true;
+ repaintComponent();
+ }
+ }
+
+ @Override
+ public void mouseOut(Component component) {
+ super.mouseOut(component);
+
+ if (Mouse.isPressed(Mouse.Button.LEFT)) {
+ // The user is currently dragging the handle. We don't
+ // un-highlight it until the user releases the left mouse
+ // button. NOTE the code that actually sets the scroll bar's
+ // value during the drag operation is handled by ScrollBarSkin
+ // since it needs access to scroll bar layout information
+ Display display = component.getDisplay();
+ display.getComponentMouseButtonListeners().add(displayMouseHandler);
+ } else {
+ // If we're not dragging the handle, then we un-highlight it
+ // as soon as the mouse exits
+ highlighted = false;
+ repaintComponent();
+ }
+ }
+ }
+
+ private class DisplayMouseHandler
+ implements ComponentMouseListener, ComponentMouseButtonListener {
+ public boolean mouseMove(Component component, int x, int y) {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int pixelValue;
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ pixelValue = x - dragOffset.x - scrollUpButton.getWidth() + 1;
+ } else {
+ pixelValue = y - dragOffset.y - scrollUpButton.getHeight() + 1;
+ }
+
+ int realValue = (int)((float)pixelValue / getValueScale());
+
+ int rangeEnd = scrollBar.getRangeEnd();
+ int extent = scrollBar.getExtent();
+
+ scrollBar.setValue(Math.min(Math.max(realValue, 0), rangeEnd - extent));
+
+ return false;
+ }
+
+ public void mouseOver(Component component) {
+ }
+
+ public void mouseOut(Component component) {
+ }
+
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ return false;
+ }
+
+ public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
+ if (button == Mouse.Button.LEFT) {
+ assert (component instanceof Display);
+ component.getComponentMouseListeners().remove(this);
+ component.getComponentMouseButtonListeners().remove(this);
+ }
+
+ return false;
+ }
+
+ public void mouseClick(Component component, Mouse.Button button, int x, int y,
+ int count) {
+ }
+ }
+
+ private static final int DEFAULT_THICKNESS = 15;
+ private static final int DEFAULT_LENGTH = 100;
+
+ private AutomaticScroller automaticScroller = new AutomaticScroller();
+
+ private DisplayMouseHandler displayMouseHandler = new DisplayMouseHandler();
+ private Point dragOffset = null;
+
+ private ScrollButton scrollUpButton = new ScrollButton(-1, new ScrollUpImage());
+ private ScrollButton scrollDownButton = new ScrollButton(1, new ScrollDownImage());
+ private ScrollHandle scrollHandle = new ScrollHandle();
+
+ private int minimumHandleLength;
+ private Color borderColor;
+ private Color scrollButtonImageColor;
+ private Color scrollButtonBackgroundColor;
+ private Color scrollButtonDisabledBackgroundColor;
+ private Color scrollButtonPressedBackgroundColor;
+ private Color scrollButtonHighlightedBackgroundColor;
+
+ public TerraScrollBarSkin() {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ minimumHandleLength = 31;
+ borderColor = theme.getColor(7);
+ scrollButtonImageColor = theme.getColor(1);
+ scrollButtonBackgroundColor = theme.getColor(10);
+ scrollButtonDisabledBackgroundColor = theme.getColor(10);
+ scrollButtonPressedBackgroundColor = theme.getColor(9);
+ scrollButtonHighlightedBackgroundColor = theme.getColor(11);
+
+ setBackgroundColor(theme.getColor(9));
+ }
+
+ @Override
+ public void install(Component component) {
+ super.install(component);
+
+ ScrollBar scrollBar = (ScrollBar)component;
+ scrollBar.getScrollBarListeners().add(this);
+ scrollBar.getScrollBarValueListeners().add(this);
+
+ scrollBar.add(scrollUpButton);
+ scrollBar.add(scrollDownButton);
+ scrollBar.add(scrollHandle);
+
+ enabledChanged(scrollBar);
+ }
+
+ @Override
+ public void uninstall() {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+ scrollBar.getScrollBarListeners().remove(this);
+ scrollBar.getScrollBarValueListeners().remove(this);
+
+ scrollBar.remove(scrollUpButton);
+ scrollBar.remove(scrollDownButton);
+ scrollBar.remove(scrollHandle);
+
+ super.uninstall();
+ }
+
+ public int getPreferredWidth(int height) {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int preferredWidth = 0;
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ preferredWidth = DEFAULT_LENGTH;
+ } else {
+ preferredWidth = DEFAULT_THICKNESS;
+ }
+
+ return preferredWidth;
+ }
+
+ public int getPreferredHeight(int width) {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int preferredHeight = 0;
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ preferredHeight = DEFAULT_THICKNESS;
+ } else {
+ preferredHeight = DEFAULT_LENGTH;
+ }
+
+ return preferredHeight;
+ }
+
+ public Dimensions getPreferredSize() {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int preferredWidth = 0;
+ int preferredHeight = 0;
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ preferredWidth = DEFAULT_LENGTH;
+ preferredHeight = DEFAULT_THICKNESS;
+ } else {
+ preferredWidth = DEFAULT_THICKNESS;
+ preferredHeight = DEFAULT_LENGTH;
+ }
+
+ return new Dimensions(preferredWidth, preferredHeight);
+ }
+
+ public void layout() {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int width = getWidth();
+ int height = getHeight();
+
+ int rangeStart = scrollBar.getRangeStart();
+ int rangeEnd = scrollBar.getRangeEnd();
+ int extent = scrollBar.getExtent();
+ int value = scrollBar.getValue();
+
+ int maxLegalRealValue = rangeEnd - extent;
+ int numLegalRealValues = maxLegalRealValue - rangeStart + 1;
+ float extentPercentage = (float)extent / (float)(rangeEnd - rangeStart);
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ scrollUpButton.setSize(scrollUpButton.getPreferredWidth(-1), height);
+ scrollUpButton.setLocation(0, 0);
+
+ scrollDownButton.setSize(scrollDownButton.getPreferredWidth(-1), height);
+ scrollDownButton.setLocation(width - scrollDownButton.getWidth(), 0);
+
+ if (scrollBar.isEnabled()) {
+ // Calculate the handle width first, as it dictates how much
+ // room is left to represent the range of legal values. Note
+ // that the handle may overlap each scroll button by 1px so
+ // that its borders merge into the borders of the scroll buttons
+ int availableWidth = width - scrollUpButton.getWidth() -
+ scrollDownButton.getWidth() + 2;
+ int handleWidth = Math.max(minimumHandleLength,
+ Math.round(extentPercentage * (float)availableWidth));
+
+ // Calculate the position of the handle by calculating the
+ // scale that maps logical value to pixel value
+ int numLegalPixelValues = availableWidth - handleWidth + 1;
+ float valueScale = (float)numLegalPixelValues / (float)numLegalRealValues;
+ int handleX = (int)((float)value * valueScale) +
+ scrollUpButton.getWidth() - 1;
+
+ if (handleWidth > availableWidth) {
+ // If we can't fit the handle, we hide it
+ scrollHandle.setVisible(false);
+ } else {
+ scrollHandle.setVisible(true);
+
+ scrollHandle.setSize(handleWidth, height);
+ scrollHandle.setLocation(handleX, 0);
+ }
+ } else {
+ scrollHandle.setVisible(false);
+ }
+ } else {
+ scrollUpButton.setSize(width, scrollUpButton.getPreferredHeight(-1));
+ scrollUpButton.setLocation(0, 0);
+
+ scrollDownButton.setSize(width, scrollDownButton.getPreferredHeight(-1));
+ scrollDownButton.setLocation(0, height - scrollDownButton.getHeight());
+
+ if (scrollBar.isEnabled()) {
+ // Calculate the handle height first, as it dictates how much
+ // room is left to represent the range of legal values. Note
+ // that the handle may overlap each scroll button by 1px so
+ // that its borders merge into the borders of the scroll buttons
+ int availableHeight = height - scrollUpButton.getHeight() -
+ scrollDownButton.getHeight() + 2;
+ int handleHeight = Math.max(minimumHandleLength,
+ Math.round(extentPercentage * (float)availableHeight));
+
+ // Calculate the position of the handle by calculating the
+ // scale maps logical value to pixel value
+ int numLegalPixelValues = availableHeight - handleHeight + 1;
+ float valueScale = (float)numLegalPixelValues / (float)numLegalRealValues;
+ int handleY = (int)((float)value * valueScale) +
+ scrollUpButton.getHeight() - 1;
+
+ if (handleHeight > availableHeight) {
+ // If we can't fit the handle, we hide it
+ scrollHandle.setVisible(false);
+ } else {
+ scrollHandle.setVisible(true);
+
+ scrollHandle.setSize(width, handleHeight);
+ scrollHandle.setLocation(0, handleY);
+ }
+ } else {
+ scrollHandle.setVisible(false);
+ }
+ }
+ }
+
+ @Override
+ public void paint(Graphics2D graphics) {
+ super.paint(graphics);
+
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int width = getWidth();
+ int height = getHeight();
+
+ graphics.setStroke(new BasicStroke());
+ graphics.setPaint(borderColor);
+
+ // Paint the scroll bar border lines
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ int scrollUpButtonWidth = scrollUpButton.getWidth();
+ int scrollDownButtonWidth = scrollDownButton.getWidth();
+
+ graphics.drawLine(scrollUpButtonWidth, 0,
+ width - scrollDownButtonWidth - 1, 0);
+ graphics.drawLine(scrollUpButtonWidth, height - 1,
+ width - scrollDownButtonWidth - 1, height - 1);
+ } else {
+ int scrollUpButtonHeight = scrollUpButton.getHeight();
+ int scrollDownButtonHeight = scrollDownButton.getHeight();
+
+ graphics.drawLine(0, scrollUpButtonHeight, 0,
+ height - scrollDownButtonHeight - 1);
+ graphics.drawLine(width - 1, scrollUpButtonHeight, width - 1,
+ height - scrollDownButtonHeight - 1);
+ }
+ }
+
+ public int getMinimumHandleLength() {
+ return minimumHandleLength;
+ }
+
+ public void setMinimumHandleLength(int minimumHandleLength) {
+ if (minimumHandleLength != this.minimumHandleLength) {
+ this.minimumHandleLength = minimumHandleLength;
+ repaintComponent();
+ }
+ }
+
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ repaintComponent();
+ }
+
+ public final void setBorderColor(String borderColor) {
+ if (borderColor == null) {
+ throw new IllegalArgumentException("borderColor is null");
+ }
+
+ setBorderColor(decodeColor(borderColor));
+ }
+
+ public Color getScrollButtonImageColor() {
+ return scrollButtonImageColor;
+ }
+
+ public void setScrollButtonImageColor(Color scrollButtonImageColor) {
+ this.scrollButtonImageColor = scrollButtonImageColor;
+ repaintComponent();
+ }
+
+ public final void setScrollButtonImageColor(String scrollButtonImageColor) {
+ if (scrollButtonImageColor == null) {
+ throw new IllegalArgumentException("scrollButtonImageColor is null");
+ }
+
+ setScrollButtonImageColor(decodeColor(scrollButtonImageColor));
+ }
+
+ public Color getScrollButtonBackgroundColor() {
+ return scrollButtonBackgroundColor;
+ }
+
+ public void setScrollButtonBackgroundColor(Color scrollButtonBackgroundColor) {
+ this.scrollButtonBackgroundColor = scrollButtonBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setScrollButtonBackgroundColor(String scrollButtonBackgroundColor) {
+ if (scrollButtonBackgroundColor == null) {
+ throw new IllegalArgumentException("scrollButtonBackgroundColor is null");
+ }
+
+ setScrollButtonBackgroundColor(decodeColor(scrollButtonBackgroundColor));
+ }
+
+ public Color getScrollButtonDisabledBackgroundColor() {
+ return scrollButtonDisabledBackgroundColor;
+ }
+
+ public void setScrollButtonDisabledBackgroundColor(Color scrollButtonDisabledBackgroundColor) {
+ this.scrollButtonDisabledBackgroundColor = scrollButtonDisabledBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setScrollButtonDisabledBackgroundColor(String scrollButtonDisabledBackgroundColor) {
+ if (scrollButtonDisabledBackgroundColor == null) {
+ throw new IllegalArgumentException("scrollButtonDisabledBackgroundColor is null");
+ }
+
+ setScrollButtonDisabledBackgroundColor(decodeColor(scrollButtonDisabledBackgroundColor));
+ }
+
+ public Color getScrollButtonPressedBackgroundColor() {
+ return scrollButtonPressedBackgroundColor;
+ }
+
+ public void setScrollButtonPressedBackgroundColor(Color scrollButtonPressedBackgroundColor) {
+ this.scrollButtonPressedBackgroundColor = scrollButtonPressedBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setScrollButtonPressedBackgroundColor(String scrollButtonPressedBackgroundColor) {
+ if (scrollButtonPressedBackgroundColor == null) {
+ throw new IllegalArgumentException("scrollButtonPressedBackgroundColor is null");
+ }
+
+ setScrollButtonPressedBackgroundColor(decodeColor(scrollButtonPressedBackgroundColor));
+ }
+
+ public Color getScrollButtonHighlightedBackgroundColor() {
+ return scrollButtonHighlightedBackgroundColor;
+ }
+
+ public void setScrollButtonHighlightedBackgroundColor(Color scrollButtonHighlightedBackgroundColor) {
+ this.scrollButtonHighlightedBackgroundColor = scrollButtonHighlightedBackgroundColor;
+ repaintComponent();
+ }
+
+ public final void setScrollButtonHighlightedBackgroundColor(String scrollButtonHighlightedBackgroundColor) {
+ if (scrollButtonHighlightedBackgroundColor == null) {
+ throw new IllegalArgumentException("scrollButtonHighlightedBackgroundColor is null");
+ }
+
+ setScrollButtonHighlightedBackgroundColor(decodeColor(scrollButtonHighlightedBackgroundColor));
+ }
+
+ @Override
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseDown(component, button, x, y);
+
+ if (scrollHandle.isVisible()
+ && button == Mouse.Button.LEFT) {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ Component mouseDownComponent = scrollBar.getComponentAt(x, y);
+
+ if (mouseDownComponent == scrollHandle) {
+ // Begin dragging the scroll handle. Register our display
+ // mouse handler to do the actual work
+ Display display = scrollBar.getDisplay();
+
+ dragOffset = scrollBar.mapPointToAncestor(display, x, y);
+ dragOffset.translate(-scrollHandle.getX(), -scrollHandle.getY());
+
+ display.getComponentMouseListeners().add(displayMouseHandler);
+ display.getComponentMouseButtonListeners().add(displayMouseHandler);
+ } else if (mouseDownComponent == null) {
+ // Begin automatic block scrolling. Calculate the direction of
+ // the scroll by checking to see if the user pressed the mouse
+ // in the area "before" the handle or "after" it.
+
+ int direction;
+ int realStopValue;
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ direction = x < scrollHandle.getX() ? -1 : 1;
+
+ int pixelStopValue = x - scrollUpButton.getWidth() + 1;
+
+ if (direction == 1) {
+ // If we're scrolling down, account for the width of the
+ // handle in our pixel stop value so that we stop as soon
+ // as the *bottom* of the handle reaches our click point
+ pixelStopValue -= scrollHandle.getWidth();
+ }
+
+ realStopValue = (int)((float)pixelStopValue / getValueScale());
+ } else {
+ direction = y < scrollHandle.getY() ? -1 : 1;
+
+ int pixelStopValue = y - scrollUpButton.getHeight() + 1;
+
+ if (direction == 1) {
+ // If we're scrolling down, account for the height of the
+ // handle in our pixel stop value so that we stop as soon
+ // as the *bottom* of the handle reaches our click point
+ pixelStopValue -= scrollHandle.getHeight();
+ }
+
+ realStopValue = (int)((float)pixelStopValue / getValueScale());
+ }
+
+ // Start the automatic scroller; we'll stop it upon mouse out or
+ // mouse up
+ automaticScroller.start(direction,
+ IncrementType.BLOCK, realStopValue);
+ }
+
+ consumed = true;
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public void mouseOut(Component component) {
+ super.mouseOut(component);
+
+ automaticScroller.stop();
+ }
+
+ @Override
+ public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
+ boolean consumed = super.mouseUp(component, button, x, y);
+
+ if (button == Mouse.Button.LEFT) {
+ automaticScroller.stop();
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public boolean mouseWheel(Component component, Mouse.ScrollType scrollType, int scrollAmount,
+ int wheelRotation, int x, int y) {
+ boolean consumed = false;
+
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ int previousValue = scrollBar.getValue();
+ int newValue = previousValue + (scrollAmount * wheelRotation *
+ scrollBar.getUnitIncrement());
+
+ if (wheelRotation > 0) {
+ int maxValue = scrollBar.getRangeEnd() - scrollBar.getExtent();
+ newValue = Math.min(newValue, maxValue);
+
+ if (previousValue < maxValue) {
+ consumed = true;
+ }
+ } else {
+ newValue = Math.max(newValue, 0);
+
+ if (previousValue > 0) {
+ consumed = true;
+ }
+ }
+
+ scrollBar.setValue(newValue);
+
+ return consumed;
+ }
+
+ /**
+ * Gets the scale factor that allows us to translate pixel values to scroll
+ * bar values and vice versa. This assumes that the range of pixels spans
+ * from the last pixel of <tt>scrollUpButton</tt> to the first pixel of
+ * <tt>scrollDownButton</tt> and excludes the pixels taken up by
+ * <tt>scrollHandle</tt>.
+ * <p>
+ * To map from scroll bar values (<i>real values</i>) to pixel values, you
+ * multiply by the value scale. To map from pixel values back to real
+ * values, you divide by the value scale.
+ *
+ * @return
+ * <tt><number of legal pixel values> / <number of legal real values></tt>
+ */
+ private float getValueScale() {
+ ScrollBar scrollBar = (ScrollBar)getComponent();
+
+ float valueScale;
+
+ int rangeStart = scrollBar.getRangeStart();
+ int rangeEnd = scrollBar.getRangeEnd();
+ int extent = scrollBar.getExtent();
+ int maxLegalRealValue = rangeEnd - extent;
+
+ int numLegalRealValues = maxLegalRealValue - rangeStart + 1;
+ int numLegalPixelValues;
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ int availableWidth = getWidth() - scrollUpButton.getWidth() -
+ scrollDownButton.getWidth() + 2;
+ numLegalPixelValues = availableWidth - scrollHandle.getWidth() + 1;
+ } else {
+ int availableHeight = getHeight() - scrollUpButton.getHeight() -
+ scrollDownButton.getHeight() + 2;
+ numLegalPixelValues = availableHeight - scrollHandle.getHeight() + 1;
+ }
+
+ valueScale = (float)numLegalPixelValues / (float)numLegalRealValues;
+
+ return valueScale;
+ }
+
+ @Override
+ public void enabledChanged(Component component) {
+ boolean enabled = component.isEnabled();
+
+ scrollUpButton.setEnabled(enabled);
+ scrollDownButton.setEnabled(enabled);
+
+ invalidateComponent();
+ }
+
+ // ScrollBarListener methods
+
+ public void orientationChanged(ScrollBar scrollBar, Orientation previousOrientation) {
+ invalidateComponent();
+ }
+
+ public void scopeChanged(ScrollBar scrollBar, int previousRangeStart,
+ int previousRangeEnd, int previousExtent) {
+ invalidateComponent();
+ }
+
+ public void unitIncrementChanged(ScrollBar scrollBar, int previousUnitIncrement) {
+ // No-op
+ }
+
+ public void blockIncrementChanged(ScrollBar scrollBar, int previousBlockIncrement) {
+ // No-op
+ }
+
+ // ScrollBarValueListener methods
+
+ public void valueChanged(ScrollBar scrollBar, int previousValue) {
+ if (scrollHandle.isVisible()) {
+ int value = scrollBar.getValue();
+
+ if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
+ int handleX = (int)((float)value * getValueScale()) +
+ scrollUpButton.getWidth() - 1;
+
+ scrollHandle.setLocation(handleX, 0);
+ } else {
+ int handleY = (int)((float)value * getValueScale()) +
+ scrollUpButton.getHeight() - 1;
+
+ scrollHandle.setLocation(0, handleY);
+ }
+ }
+ }
+}
Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollPaneCornerSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollPaneCornerSkin.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollPaneCornerSkin.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollPaneCornerSkin.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+
+import pivot.wtk.Dimensions;
+import pivot.wtk.skin.ComponentSkin;
+
+/**
+ * Scroll pane corner skin.
+ *
+ * @author tvolkert
+ */
+public class TerraScrollPaneCornerSkin extends ComponentSkin {
+ private Color backgroundColor = new Color(0xF0, 0xEC, 0xE7);
+ private Color color = new Color(0x81, 0x76, 0x67);
+
+ @Override
+ public boolean isFocusable() {
+ return false;
+ }
+
+ public int getPreferredWidth(int height) {
+ // ScrollPane corners have no implicit preferred size.
+ return 0;
+ }
+
+ public int getPreferredHeight(int width) {
+ // ScrollPane corners have no implicit preferred size.
+ return 0;
+ }
+
+ public Dimensions getPreferredSize() {
+ // ScrollPane corners have no implicit preferred size.
+ return new Dimensions(0, 0);
+ }
+
+ public void layout() {
+ // No-op
+ }
+
+ public void paint(Graphics2D graphics) {
+ int width = getWidth();
+ int height = getHeight();
+
+ graphics.setPaint(backgroundColor);
+ graphics.fill(new Rectangle2D.Double(0, 0, width, height));
+ }
+
+ public Color getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ public void setBackgroundColor(Color backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ repaintComponent();
+ }
+
+ public final void setBackgroundColor(String backgroundColor) {
+ if (backgroundColor == null) {
+ throw new IllegalArgumentException("backgroundColor is null.");
+ }
+
+ setBackgroundColor(decodeColor(backgroundColor));
+ }
+
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ repaintComponent();
+ }
+
+ public final void setColor(String color) {
+ if (color == null) {
+ throw new IllegalArgumentException("color is null.");
+ }
+
+ setColor(decodeColor(color));
+ }
+}
Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollPaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollPaneSkin.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollPaneSkin.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraScrollPaneSkin.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import pivot.wtk.Theme;
+import pivot.wtk.skin.ScrollPaneSkin;
+
+/**
+ * Scroll pane skin that applies terra-specific colors.
+ */
+public class TerraScrollPaneSkin extends ScrollPaneSkin {
+ public final void setBackgroundColor(int backgroundColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setBackgroundColor(theme.getColor(backgroundColor));
+ }
+}
Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraSeparatorSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraSeparatorSkin.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraSeparatorSkin.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraSeparatorSkin.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import pivot.wtk.Theme;
+import pivot.wtk.skin.SeparatorSkin;
+
+/**
+ * Terra sheet skin.
+ *
+ * @author gbrown
+ */
+public class TerraSeparatorSkin extends SeparatorSkin {
+ public TerraSeparatorSkin() {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setColor(theme.getColor(7));
+ setHeadingColor(theme.getColor(13));
+ }
+
+ public void setColor(int color) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setColor(theme.getColor(color));
+ }
+
+ public void setHeadingColor(int headingColor) {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+ setHeadingColor(theme.getColor(headingColor));
+ }
+}
Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraSheetSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraSheetSkin.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraSheetSkin.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraSheetSkin.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.skin.terra;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+
+import pivot.collections.Dictionary;
+import pivot.util.Vote;
+import pivot.wtk.ApplicationContext;
+import pivot.wtk.Component;
+import pivot.wtk.ComponentMouseButtonListener;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Insets;
+import pivot.wtk.Keyboard;
+import pivot.wtk.Mouse;
+import pivot.wtk.Sheet;
+import pivot.wtk.SheetStateListener;
+import pivot.wtk.Theme;
+import pivot.wtk.Window;
+import pivot.wtk.effects.DropShadowDecorator;
+import pivot.wtk.effects.Transition;
+import pivot.wtk.effects.TransitionListener;
+import pivot.wtk.skin.WindowSkin;
+
+/**
+ * Sheet skin class.
+ * <p>
+ * TODO Wire up the "resizable" flag. It current exists but does nothing.
+ *
+ * @author gbrown
+ * @author tvolkert
+ */
+public class TerraSheetSkin extends WindowSkin implements SheetStateListener {
+ private Color borderColor;
+ private Insets padding;
+ private boolean resizable;
+
+ // Derived colors
+ private Color bevelColor;
+
+ private SlideTransition openTransition = null;
+ private SlideTransition closeTransition = null;
+
+ private ComponentMouseButtonListener ownerMouseButtonListener =
+ new ComponentMouseButtonListener() {
+ public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+ Window owner = (Window)component;
+ Component ownerContent = owner.getContent();
+
+ if (ownerContent != null
+ && !ownerContent.isEnabled()
+ && owner.getComponentAt(x, y) == ownerContent) {
+ ApplicationContext.beep();
+ }
+
+ return false;
+ }
+
+ public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
+ return false;
+ }
+
+ public void mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+ // No-op
+ }
+ };
+
+ private DropShadowDecorator dropShadowDecorator = null;
+
+ private static final int SLIDE_DURATION = 250;
+ private static final int SLIDE_RATE = 30;
+
+ public TerraSheetSkin() {
+ TerraTheme theme = (TerraTheme)Theme.getTheme();
+
+ Color backgroundColor = theme.getColor(11);
+ backgroundColor = new Color(backgroundColor.getRed(), backgroundColor.getGreen(),
+ backgroundColor.getBlue(), 0xF0);
+ setBackgroundColor(backgroundColor);
+
+ borderColor = theme.getColor(7);
+ padding = new Insets(8);
+ resizable = false;
+
+ // Set the derived colors
+ bevelColor = TerraTheme.darken(backgroundColor);
+ }
+
+ @Override
+ public void install(Component component) {
+ super.install(component);
+
+ Sheet sheet = (Sheet)component;
+ sheet.getSheetStateListeners().add(this);
+
+ // Attach the drop-shadow decorator
+ dropShadowDecorator = new DropShadowDecorator(3, 3, 3);
+ sheet.getDecorators().add(dropShadowDecorator);
+ }
+
+ @Override
+ public void uninstall() {
+ Sheet sheet = (Sheet)getComponent();
+ sheet.getSheetStateListeners().remove(this);
+
+ // Detach the drop shadow decorator
+ sheet.getDecorators().remove(dropShadowDecorator);
+ dropShadowDecorator = null;
+
+ super.uninstall();
+ }
+
+ @Override
+ public int getPreferredWidth(int height) {
+ int preferredWidth = 0;
+
+ Sheet sheet = (Sheet)getComponent();
+ Component content = sheet.getContent();
+
+ if (content != null
+ && content.isDisplayable()) {
+ if (height != -1) {
+ height = Math.max(height - (padding.top + padding.bottom + 2), 0);
+ }
+
+ preferredWidth = content.getPreferredWidth(height);
+ }
+
+ preferredWidth += (padding.left + padding.right + 2);
+
+ return preferredWidth;
+ }
+
+ @Override
+ public int getPreferredHeight(int width) {
+ int preferredHeight = 0;
+
+ Sheet sheet = (Sheet)getComponent();
+ Component content = sheet.getContent();
+
+ if (content != null
+ && content.isDisplayable()) {
+ if (width != -1) {
+ width = Math.max(width - (padding.left + padding.right + 2), 0);
+ }
+
+ preferredHeight = content.getPreferredHeight(width);
+ }
+
+ preferredHeight += (padding.top + padding.bottom + 2);
+
+ return preferredHeight;
+ }
+
+ @Override
+ public Dimensions getPreferredSize() {
+ int preferredWidth = 0;
+ int preferredHeight = 0;
+
+ Sheet sheet = (Sheet)getComponent();
+ Component content = sheet.getContent();
+
+ if (content != null
+ && content.isDisplayable()) {
+ Dimensions preferredContentSize = content.getPreferredSize();
+ preferredWidth = preferredContentSize.width;
+ preferredHeight = preferredContentSize.height;
+ }
+
+ preferredWidth += (padding.left + padding.right + 2);
+ preferredHeight += (padding.top + padding.bottom + 2);
+
+ Dimensions preferredSize = new Dimensions(preferredWidth, preferredHeight);
+
+ return preferredSize;
+ }
+
+ public void layout() {
+ int width = getWidth();
+ int height = getHeight();
+
+ Sheet sheet = (Sheet)getComponent();
+ Component content = sheet.getContent();
+
+ if (content != null) {
+ if (content.isDisplayable()) {
+ content.setVisible(true);
+
+ content.setLocation(padding.left + 1, padding.top + 1);
+
+ int contentWidth = Math.max(width - (padding.left + padding.right + 2), 0);
+ int contentHeight = Math.max(height - (padding.top + padding.bottom + 2), 0);
+
+ content.setSize(contentWidth, contentHeight);
+ } else {
+ content.setVisible(false);
+ }
+ }
+ }
+
+ @Override
+ public void paint(Graphics2D graphics) {
+ super.paint(graphics);
+
+ int width = getWidth();
+ int height = getHeight();
+
+ graphics.setPaint(borderColor);
+ graphics.drawRect(0, 0, width - 1, height - 1);
+
+ graphics.setPaint(bevelColor);
+ graphics.drawLine(1, height - 2, width - 2, height - 2);
+ }
+
+ @Override
+ public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+ boolean consumed = false;
+
+ Sheet sheet = (Sheet)getComponent();
+
+ if (keyCode == Keyboard.KeyCode.ENTER) {
+ sheet.close(true);
+ consumed = true;
+ } else if (keyCode == Keyboard.KeyCode.ESCAPE) {
+ sheet.close(false);
+ consumed = true;
+ } else {
+ consumed = super.keyPressed(component, keyCode, keyLocation);
+ }
+
+ return consumed;
+ }
+
+ @Override
+ public void setBackgroundColor(Color backgroundColor) {
+ super.setBackgroundColor(backgroundColor);
+ bevelColor = TerraTheme.darken(backgroundColor);
+ }
+
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ if (borderColor == null) {
+ throw new IllegalArgumentException("borderColor is null.");
+ }
+
+ this.borderColor = borderColor;
+ repaintComponent();
+ }
+
+ public final void setBorderColor(String borderColor) {
+ if (borderColor == null) {
+ throw new IllegalArgumentException("borderColor is null.");
+ }
+
+ setBorderColor(decodeColor(borderColor));
+ }
+
+ public Insets getPadding() {
+ return padding;
+ }
+
+ public void setPadding(Insets padding) {
+ if (padding == null) {
+ throw new IllegalArgumentException("padding is null.");
+ }
+
+ this.padding = padding;
+ invalidateComponent();
+ }
+
+ public final void setPadding(Dictionary<String, ?> padding) {
+ if (padding == null) {
+ throw new IllegalArgumentException("padding is null.");
+ }
+
+ setPadding(new Insets(padding));
+ }
+
+ public final void setPadding(int padding) {
+ setPadding(new Insets(padding));
+ }
+
+ public final void setPadding(Number padding) {
+ if (padding == null) {
+ throw new IllegalArgumentException("padding is null.");
+ }
+
+ setPadding(padding.intValue());
+ }
+
+ public boolean isResizable() {
+ return resizable;
+ }
+
+ public void setResizable(boolean resizable) {
+ this.resizable = resizable;
+ invalidateComponent();
+ }
+
+ @Override
+ public void windowOpened(final Window window) {
+ super.windowOpened(window);
+
+ Window owner = window.getOwner();
+ owner.getComponentMouseButtonListeners().add(ownerMouseButtonListener);
+
+ ApplicationContext.queueCallback(new Runnable() {
+ public void run() {
+ openTransition = new SlideTransition(window, 0, 0,
+ -window.getHeight(), 0, false, SLIDE_DURATION, SLIDE_RATE);
+ openTransition.start(new TransitionListener() {
+ public void transitionCompleted(Transition transition) {
+ openTransition = null;
+ }
+ });
+ }
+ });
+ }
+
+ public Vote previewSheetClose(final Sheet sheet, final boolean result) {
+ // Start a close transition, return false, and close the window
+ // when the transition is complete
+ Vote vote = Vote.APPROVE;
+
+ if (closeTransition == null) {
+ int duration = SLIDE_DURATION;
+ int beginX = 0;
+ int beginY = 0;
+
+ if (openTransition != null) {
+ // Stop the open transition
+ openTransition.stop();
+
+ // Record its progress so we can reverse it at the right point
+ duration = openTransition.getElapsedTime();
+ beginX = openTransition.getX();
+ beginY = openTransition.getY();
+
+ openTransition = null;
+ }
+
+ if (duration > 0) {
+ closeTransition = new SlideTransition(sheet, beginX, 0,
+ beginY, -sheet.getHeight(), true, duration, SLIDE_RATE);
+ closeTransition.start(new TransitionListener() {
+ public void transitionCompleted(Transition transition) {
+ sheet.close(result);
+ closeTransition = null;
+ }
+ });
+
+ vote = Vote.DEFER;
+ }
+ } else {
+ vote = (closeTransition.isRunning()) ? Vote.DEFER : Vote.APPROVE;
+ }
+
+ return vote;
+ }
+
+ public void sheetCloseVetoed(Sheet sheet, Vote reason) {
+ if (reason == Vote.DENY
+ && closeTransition != null) {
+ closeTransition.stop();
+ }
+ }
+
+ public void sheetClosed(Sheet sheet) {
+ Window owner = sheet.getOwner();
+ owner.getComponentMouseButtonListeners().remove(ownerMouseButtonListener);
+
+ closeTransition = null;
+ }
+}