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:36:28 UTC

svn commit: r754936 [34/38] - in /incubator/pivot/tags/v1.0.1: ./ 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/c...

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraSpinnerSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraSpinnerSkin.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraSpinnerSkin.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraSpinnerSkin.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,813 @@
+/*
+ * 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.Font;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+
+import pivot.collections.List;
+import pivot.wtk.ApplicationContext;
+import pivot.wtk.Component;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Keyboard;
+import pivot.wtk.Mouse;
+import pivot.wtk.Bounds;
+import pivot.wtk.Spinner;
+import pivot.wtk.SpinnerListener;
+import pivot.wtk.SpinnerSelectionListener;
+import pivot.wtk.Theme;
+import pivot.wtk.media.Image;
+import pivot.wtk.skin.ComponentSkin;
+import pivot.wtk.skin.ContainerSkin;
+
+/**
+ * Spinner skin.
+ *
+ * @author tvolkert
+ */
+public class TerraSpinnerSkin extends ContainerSkin implements Spinner.Skin,
+    SpinnerListener, SpinnerSelectionListener {
+    /**
+     * Encapsulates the code needed to perform timer-controlled spinning.
+     */
+    private static class AutomaticSpinner {
+        public Spinner spinner;
+        public int direction;
+
+        private int timeoutID = -1;
+        private int intervalID = -1;
+
+        /**
+         * Starts spinning the specified spinner.
+         *
+         * @param spinner
+         * The spinner to spin
+         *
+         * @param direction
+         * <tt>1</tt> to adjust the spinner's selected index larger;
+         * <tt>-1</tt> to adjust it smaller
+         *
+         * @exception IllegalStateException
+         * If automatic spinner of any spinner is already in progress.
+         * Only one spinner may be automatically spun at one time
+         */
+        public void start(Spinner spinner, int direction) {
+            assert(direction != 0) : "Direction must be positive or negative";
+
+            if (timeoutID != -1
+                || intervalID != -1) {
+                throw new IllegalStateException("Already running");
+            }
+
+            this.spinner = spinner;
+            this.direction = direction;
+
+            // Wait a timeout period, then begin repidly spinning
+            timeoutID = ApplicationContext.setTimeout(new Runnable() {
+                public void run() {
+                    intervalID = ApplicationContext.setInterval(new Runnable() {
+                        public void run() {
+                            spin();
+                        }
+                    }, 30);
+
+                    timeoutID = -1;
+                }
+            }, 400);
+
+            // We initially spin once to register that we've started
+            spin();
+        }
+
+        private void spin() {
+            boolean circular = spinner.isCircular();
+            int selectedIndex = spinner.getSelectedIndex();
+            int count = spinner.getSpinnerData().getLength();
+
+            if (direction > 0) {
+                if (selectedIndex < count - 1) {
+                    spinner.setSelectedIndex(selectedIndex + 1);
+                } else if (circular) {
+                    spinner.setSelectedIndex(0);
+                } else {
+                    stop();
+                }
+            } else {
+                if (selectedIndex > 0) {
+                    spinner.setSelectedIndex(selectedIndex - 1);
+                } else if (circular) {
+                    spinner.setSelectedIndex(count - 1);
+                } else {
+                    stop();
+                }
+            }
+        }
+
+        /**
+         * Stops any automatic spinning in progress.
+         */
+        public void stop() {
+            if (timeoutID != -1) {
+                ApplicationContext.clearTimeout(timeoutID);
+                timeoutID = -1;
+            }
+
+            if (intervalID != -1) {
+                ApplicationContext.clearInterval(intervalID);
+                intervalID = -1;
+            }
+        }
+    }
+
+    /**
+     * Component that holds the content of a spinner. It is the focusable part
+     * of a spinner.
+     *
+     * @author tvolkert
+     */
+    protected class SpinnerContent extends Component {
+        public SpinnerContent() {
+            setSkin(new SpinnerContentSkin());
+        }
+    }
+
+    /**
+     * SpinnerContent skin.
+     *
+     * @author tvolkert
+     */
+    protected class SpinnerContentSkin extends ComponentSkin {
+        public int getPreferredWidth(int height) {
+            Spinner spinner = (Spinner)TerraSpinnerSkin.this.getComponent();
+            Spinner.ItemRenderer itemRenderer = spinner.getItemRenderer();
+
+            itemRenderer.render(spinner.getSelectedValue(), spinner);
+            return itemRenderer.getPreferredWidth(height);
+        }
+
+        public int getPreferredHeight(int width) {
+            Spinner spinner = (Spinner)TerraSpinnerSkin.this.getComponent();
+            Spinner.ItemRenderer itemRenderer = spinner.getItemRenderer();
+
+            itemRenderer.render(spinner.getSelectedValue(), spinner);
+            return itemRenderer.getPreferredHeight(width);
+        }
+
+        public Dimensions getPreferredSize() {
+            Spinner spinner = (Spinner)TerraSpinnerSkin.this.getComponent();
+            Spinner.ItemRenderer itemRenderer = spinner.getItemRenderer();
+
+            itemRenderer.render(spinner.getSelectedValue(), spinner);
+            return itemRenderer.getPreferredSize();
+        }
+
+        public void layout() {
+            // No-op
+        }
+
+        public void paint(Graphics2D graphics) {
+            SpinnerContent spinnerContent = (SpinnerContent)getComponent();
+            Spinner spinner = (Spinner)TerraSpinnerSkin.this.getComponent();
+
+            int width = getWidth();
+            int height = getHeight();
+
+            // Paint the content
+            Spinner.ItemRenderer itemRenderer = spinner.getItemRenderer();
+            itemRenderer.render(spinner.getSelectedValue(), spinner);
+
+            Graphics2D contentGraphics = (Graphics2D)graphics.create();
+            Dimensions rendererSize = itemRenderer.getPreferredSize();
+            itemRenderer.setSize(rendererSize.width, rendererSize.height);
+            itemRenderer.paint(contentGraphics);
+            contentGraphics.dispose();
+
+            // Paint the focus state
+            if (spinnerContent.isFocused()) {
+                BasicStroke dashStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND,
+                    BasicStroke.JOIN_ROUND, 1.0f, new float[] {0.0f, 2.0f}, 0.0f);
+
+                graphics.setStroke(dashStroke);
+                graphics.setColor(borderColor);
+
+                graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                    RenderingHints.VALUE_ANTIALIAS_ON);
+
+                graphics.drawRect(1, 1, Math.max(width - 3, 0),
+                    Math.max(height - 3, 0));
+            }
+        }
+
+        @Override
+        public void enabledChanged(Component component) {
+            super.enabledChanged(component);
+
+            repaintComponent();
+        }
+
+        @Override
+        public void focusedChanged(Component component, boolean temporary) {
+            super.focusedChanged(component, temporary);
+
+            repaintComponent();
+        }
+
+        @Override
+        public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+            boolean consumed = false;
+
+            Spinner spinner = (Spinner)TerraSpinnerSkin.this.getComponent();
+
+            boolean circular = spinner.isCircular();
+            int count = spinner.getSpinnerData().getLength();
+
+            int selectedIndex = spinner.getSelectedIndex();
+            int newSelectedIndex = selectedIndex;
+
+            if (keyCode == Keyboard.KeyCode.UP) {
+                if (selectedIndex < count - 1) {
+                    newSelectedIndex++;
+                } else if (circular) {
+                    newSelectedIndex = 0;
+                }
+            } else if (keyCode == Keyboard.KeyCode.DOWN) {
+                if (selectedIndex > 0) {
+                    newSelectedIndex--;
+                } else if (circular) {
+                    newSelectedIndex = count - 1;
+                }
+            } else {
+                consumed = super.keyPressed(component, keyCode, keyLocation);
+            }
+
+            if (newSelectedIndex != selectedIndex) {
+                spinner.setSelectedIndex(newSelectedIndex);
+                consumed = true;
+            }
+
+            return consumed;
+        }
+    }
+
+    /**
+     * Spinner button.
+     *
+     * @author tvolkert
+     */
+    protected class SpinButton extends Component {
+        private int direction;
+        private Image buttonImage;
+
+        public SpinButton(int direction, Image buttonImage) {
+            this.direction = direction;
+            this.buttonImage = buttonImage;
+
+            setSkin(new SpinButtonSkin());
+        }
+
+        public int getDirection() {
+            return direction;
+        }
+
+        public Image getButtonImage() {
+            return buttonImage;
+        }
+    }
+
+    /**
+     * Spinner button skin.
+     *
+     * @author tvolkert
+     */
+    protected class SpinButtonSkin extends ComponentSkin {
+        private boolean highlighted = false;
+        private boolean pressed = false;
+
+        @Override
+        public boolean isFocusable() {
+            return false;
+        }
+
+        public int getPreferredWidth(int height) {
+            return BUTTON_IMAGE_SIZE + 6;
+        }
+
+        public int getPreferredHeight(int width) {
+            return BUTTON_IMAGE_SIZE + 2;
+        }
+
+        public Dimensions getPreferredSize() {
+            return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+        }
+
+        public void layout() {
+            // No-op
+        }
+
+        public void paint(Graphics2D graphics) {
+            // Apply spinner styles to the button
+            SpinButton spinButton = (SpinButton)getComponent();
+
+            int width = getWidth();
+            int height = getHeight();
+
+            // Paint the background
+        	float alpha = pressed ? 0.5f : highlighted ? 0.25f : 0.0f;
+        	graphics.setPaint(new Color(0, 0, 0, alpha));
+            graphics.fillRect(0, 0, width, height);
+
+            // Paint the image
+            SpinButtonImage buttonImage = (SpinButtonImage)spinButton.getButtonImage();
+            graphics.translate((width - BUTTON_IMAGE_SIZE) / 2,
+        		(height - BUTTON_IMAGE_SIZE) / 2);
+            buttonImage.paint(graphics);
+        }
+
+        @Override
+        public void enabledChanged(Component component) {
+            super.enabledChanged(component);
+
+            automaticSpinner.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);
+
+            automaticSpinner.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) {
+                SpinButton spinButton = (SpinButton)getComponent();
+                Spinner spinner = (Spinner)TerraSpinnerSkin.this.getComponent();
+
+                // Start the automatic spinner. It'll be stopped when we
+                // mouse up or mouse out
+                automaticSpinner.start(spinner, spinButton.getDirection());
+
+                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) {
+                automaticSpinner.stop();
+
+                pressed = false;
+                repaintComponent();
+            }
+
+            return consumed;
+        }
+    }
+
+    /**
+     * Abstract base class for button images.
+     */
+    protected abstract class SpinButtonImage extends Image {
+        public int getWidth() {
+            return BUTTON_IMAGE_SIZE;
+        }
+
+        public int getHeight() {
+            return BUTTON_IMAGE_SIZE;
+        }
+
+        public void paint(Graphics2D graphics) {
+            graphics.setStroke(new BasicStroke(0));
+            graphics.setPaint(buttonImageColor);
+        }
+    }
+
+    protected class SpinUpImage extends SpinButtonImage {
+        public void paint(Graphics2D graphics) {
+            super.paint(graphics);
+
+            int[] xPoints = {0, 2, 4};
+            int[] yPoints = {3, 1, 3};
+            graphics.fillPolygon(xPoints, yPoints, 3);
+            graphics.drawPolygon(xPoints, yPoints, 3);
+        }
+    }
+
+    protected class SpinDownImage extends SpinButtonImage {
+        public void paint(Graphics2D graphics) {
+            super.paint(graphics);
+
+            int[] xPoints = {0, 2, 4};
+            int[] yPoints = {1, 3, 1};
+            graphics.fillPolygon(xPoints, yPoints, 3);
+            graphics.drawPolygon(xPoints, yPoints, 3);
+        }
+    }
+
+    private SpinnerContent spinnerContent = new SpinnerContent();
+    private SpinButton upButton = new SpinButton(1, new SpinUpImage());
+    private SpinButton downButton = new SpinButton(-1, new SpinDownImage());
+
+    private Font font;
+    private Color color;
+    private Color disabledColor;
+    private Color borderColor;
+    private Color buttonImageColor;
+    private Color buttonBackgroundColor;
+    private Color buttonDisabledBackgroundColor;
+    private Color buttonHighlightedBackgroundColor;
+
+    private static AutomaticSpinner automaticSpinner = new AutomaticSpinner();
+
+    public static final int BUTTON_IMAGE_SIZE = 5;
+
+    public TerraSpinnerSkin() {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setBackgroundColor(theme.getColor(4));
+
+        font = theme.getFont();
+        color = theme.getColor(1);
+        disabledColor = theme.getColor(7);
+        borderColor = theme.getColor(7);
+        buttonImageColor = theme.getColor(1);
+        buttonBackgroundColor = theme.getColor(10);
+        buttonDisabledBackgroundColor = theme.getColor(10);
+        buttonHighlightedBackgroundColor = theme.getColor(11);
+    }
+
+    @Override
+    public void setSize(int width, int height) {
+        int previousWidth = getWidth();
+        int previousHeight = getHeight();
+
+        super.setSize(width, height);
+
+        if (previousWidth != width
+            || previousHeight != height) {
+            automaticSpinner.stop();
+        }
+    }
+
+    @Override
+    public void install(Component component) {
+        super.install(component);
+
+        Spinner spinner = (Spinner)component;
+        spinner.getSpinnerListeners().add(this);
+        spinner.getSpinnerSelectionListeners().add(this);
+
+        spinner.add(spinnerContent);
+        spinner.add(upButton);
+        spinner.add(downButton);
+    }
+
+    @Override
+    public void uninstall() {
+        Spinner spinner = (Spinner)getComponent();
+        spinner.getSpinnerListeners().remove(this);
+        spinner.getSpinnerSelectionListeners().remove(this);
+
+        spinner.remove(spinnerContent);
+        spinner.remove(upButton);
+        spinner.remove(downButton);
+
+        super.uninstall();
+    }
+
+    @Override
+    public int getPreferredWidth(int height) {
+        // Preferred width is the sum of our maximum button width plus the
+        // content width, plus the border
+
+        // Border thickness
+        int preferredWidth = 2;
+
+        int buttonHeight = (height < 0 ? -1 : height / 2);
+        preferredWidth += Math.max(upButton.getPreferredWidth(buttonHeight),
+            downButton.getPreferredWidth(buttonHeight));
+
+        if (height >= 0) {
+            // Subtract border thickness from height constraint
+            height = Math.max(height - 2, 0);
+        }
+
+        preferredWidth += spinnerContent.getPreferredWidth(height);
+
+        return preferredWidth;
+    }
+
+    @Override
+    public int getPreferredHeight(int width) {
+        // Preferred height is the maximum of the button height and the
+        // renderer's preferred height (plus the border), where button
+        // height is defined as the larger of the two buttons' preferred
+        // height, doubled.
+
+        Dimensions upButtonPreferredSize = upButton.getPreferredSize();
+        Dimensions downButtonPreferredSize = downButton.getPreferredSize();
+
+        int preferredHeight = Math.max(upButtonPreferredSize.height,
+            downButtonPreferredSize.height) * 2;
+
+        if (width >= 0) {
+            // Subtract the button and border width from width constraint
+            int buttonWidth = Math.max(upButtonPreferredSize.width,
+                downButtonPreferredSize.width);
+
+            width = Math.max(width - buttonWidth - 2, 0);
+        }
+
+        preferredHeight = Math.max(preferredHeight,
+            spinnerContent.getPreferredHeight(width)) + 1;
+
+        return preferredHeight;
+    }
+
+    @Override
+    public Dimensions getPreferredSize() {
+        // TODO Optimize
+        return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+    }
+
+    public void layout() {
+        int width = getWidth();
+        int height = getHeight();
+
+        int buttonHeight = (height - 2) / 2;
+        int buttonWidth = Math.max(upButton.getPreferredWidth(buttonHeight),
+            downButton.getPreferredWidth(buttonHeight));
+
+        spinnerContent.setSize(width - buttonWidth - 3, height - 2);
+        spinnerContent.setLocation(1, 1);
+
+        upButton.setSize(buttonWidth, buttonHeight);
+        upButton.setLocation(width - buttonWidth - 1, 1);
+
+        downButton.setSize(buttonWidth, buttonHeight);
+        downButton.setLocation(width - buttonWidth - 1, height - buttonHeight - 1);
+    }
+
+    @Override
+    public void paint(Graphics2D graphics) {
+        super.paint(graphics);
+
+        int width = getWidth();
+        int height = getHeight();
+
+        int buttonX = upButton.getX();
+        int buttonWidth = upButton.getWidth();
+        int buttonHeight = upButton.getHeight();
+
+        graphics.setPaint(new GradientPaint(buttonX + buttonWidth / 2, 0, buttonHighlightedBackgroundColor,
+    		buttonX + buttonWidth / 2, buttonHeight, buttonBackgroundColor));
+        graphics.fillRect(buttonX, 0, buttonWidth, height);
+
+        graphics.setStroke(new BasicStroke(0));
+        graphics.setPaint(borderColor);
+        graphics.drawRect(0, 0, width - 1, height - 1);
+        graphics.drawLine(width - buttonWidth - 2, 0,
+            width - buttonWidth - 2, height - 1);
+        graphics.drawLine(width - buttonWidth - 2, buttonHeight + 1,
+            width - 1, buttonHeight + 1);
+    }
+
+    @Override
+    public void mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+        spinnerContent.requestFocus();
+    }
+
+    protected void invalidateContent() {
+        spinnerContent.invalidate();
+        spinnerContent.repaint();
+    }
+
+    public Color getColor() {
+        return color;
+    }
+
+    public void setColor(Color color) {
+        if (color == null) {
+            throw new IllegalArgumentException("color is null.");
+        }
+
+        this.color = color;
+
+        repaintComponent();
+    }
+
+    public final void setColor(String color) {
+        if (color == null) {
+            throw new IllegalArgumentException("color is null.");
+        }
+
+        setColor(decodeColor(color));
+    }
+
+    public Color getDisabledColor() {
+        return disabledColor;
+    }
+
+    public void setDisabledColor(Color disabledColor) {
+        if (disabledColor == null) {
+            throw new IllegalArgumentException("disabledColor is null.");
+        }
+
+        this.disabledColor = disabledColor;
+
+        repaintComponent();
+    }
+
+    public final void setDisabledColor(String disabledColor) {
+        if (disabledColor == null) {
+            throw new IllegalArgumentException("disabledColor is null.");
+        }
+
+        setDisabledColor(decodeColor(disabledColor));
+    }
+
+    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 Color getButtonImageColor() {
+        return buttonImageColor;
+    }
+
+    public void setButtonImageColor(Color buttonImageColor) {
+        this.buttonImageColor = buttonImageColor;
+        repaintComponent();
+    }
+
+    public final void setButtonImageColor(String buttonImageColor) {
+        if (buttonImageColor == null) {
+            throw new IllegalArgumentException("buttonImageColor is null");
+        }
+
+        setButtonImageColor(decodeColor(buttonImageColor));
+    }
+
+    public Color getButtonBackgroundColor() {
+        return buttonBackgroundColor;
+    }
+
+    public void setButtonBackgroundColor(Color buttonBackgroundColor) {
+        this.buttonBackgroundColor = buttonBackgroundColor;
+        repaintComponent();
+    }
+
+    public final void setButtonBackgroundColor(String buttonBackgroundColor) {
+        if (buttonBackgroundColor == null) {
+            throw new IllegalArgumentException("buttonBackgroundColor is null");
+        }
+
+        setButtonBackgroundColor(decodeColor(buttonBackgroundColor));
+    }
+
+    public Color getButtonDisabledBackgroundColor() {
+        return buttonDisabledBackgroundColor;
+    }
+
+    public void setButtonDisabledBackgroundColor(Color buttonDisabledBackgroundColor) {
+        this.buttonDisabledBackgroundColor = buttonDisabledBackgroundColor;
+        repaintComponent();
+    }
+
+    public final void setButtonDisabledBackgroundColor(String buttonDisabledBackgroundColor) {
+        if (buttonDisabledBackgroundColor == null) {
+            throw new IllegalArgumentException("buttonDisabledBackgroundColor is null");
+        }
+
+        setButtonDisabledBackgroundColor(decodeColor(buttonDisabledBackgroundColor));
+    }
+
+    public Color getButtonHighlightedBackgroundColor() {
+        return buttonHighlightedBackgroundColor;
+    }
+
+    public void setButtonHighlightedBackgroundColor(Color buttonHighlightedBackgroundColor) {
+        this.buttonHighlightedBackgroundColor = buttonHighlightedBackgroundColor;
+        repaintComponent();
+    }
+
+    public final void setButtonHighlightedBackgroundColor(String buttonHighlightedBackgroundColor) {
+        if (buttonHighlightedBackgroundColor == null) {
+            throw new IllegalArgumentException("buttonHighlightedBackgroundColor is null");
+        }
+
+        setButtonHighlightedBackgroundColor(decodeColor(buttonHighlightedBackgroundColor));
+    }
+
+    public Font getFont() {
+        return font;
+    }
+
+    public void setFont(Font font) {
+        if (font == null) {
+            throw new IllegalArgumentException("font is null.");
+        }
+
+        this.font = font;
+
+        invalidateContent();
+    }
+
+    public final void setFont(String font) {
+        if (font == null) {
+            throw new IllegalArgumentException("font is null.");
+        }
+
+        setFont(Font.decode(font));
+    }
+
+    // Spinner.Skin methods
+
+    public Bounds getContentBounds() {
+        return spinnerContent.getBounds();
+    }
+
+    // SpinnerListener methods
+
+    public void spinnerDataChanged(Spinner spinner, List<?> previousSpinnerData) {
+        invalidateContent();
+    }
+
+    public void itemRendererChanged(Spinner spinner,
+        Spinner.ItemRenderer previousItemRenderer) {
+        invalidateContent();
+    }
+
+    public void circularChanged(Spinner spinner) {
+        // No-op
+    }
+
+    public void selectedValueKeyChanged(Spinner spinner,
+        String previousSelectedValueKey) {
+        // No-op
+    }
+
+    // SpinnerSelectionListener methods
+
+    public void selectedIndexChanged(Spinner spinner, int previousSelectedIndex) {
+        invalidateContent();
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraSplitPaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraSplitPaneSkin.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraSplitPaneSkin.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraSplitPaneSkin.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,540 @@
+/*
+ * 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 pivot.wtk.Component;
+import pivot.wtk.ComponentMouseListener;
+import pivot.wtk.ComponentMouseButtonListener;
+import pivot.wtk.Cursor;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Display;
+import pivot.wtk.Mouse;
+import pivot.wtk.Orientation;
+import pivot.wtk.Point;
+import pivot.wtk.Span;
+import pivot.wtk.SplitPane;
+import pivot.wtk.SplitPaneListener;
+import pivot.wtk.Theme;
+import pivot.wtk.skin.ComponentSkin;
+import pivot.wtk.skin.ContainerSkin;
+
+/**
+ * Split pane skin.
+ *
+ * @author tvolkert
+ */
+public class TerraSplitPaneSkin extends ContainerSkin
+    implements SplitPaneListener {
+
+    /**
+     * Split pane splitter component.
+     *
+     * @author tvolkert
+     */
+    protected class Splitter extends Component {
+        public Splitter() {
+            super();
+            setSkin(new SplitterSkin());
+        }
+
+        public void updateCursor() {
+            Cursor cursor = Cursor.DEFAULT;
+            SplitPane splitPane = (SplitPane)TerraSplitPaneSkin.this.getComponent();
+
+            if (!splitPane.isLocked()) {
+                switch (splitPane.getOrientation()) {
+                case HORIZONTAL:
+                    switch (splitPane.getPrimaryRegion()) {
+                    case TOP_LEFT:
+                        cursor = Cursor.RESIZE_EAST;
+                        break;
+                    case BOTTOM_RIGHT:
+                        cursor = Cursor.RESIZE_WEST;
+                        break;
+                    }
+                    break;
+
+                case VERTICAL:
+                    switch (splitPane.getPrimaryRegion()) {
+                    case TOP_LEFT:
+                        cursor = Cursor.RESIZE_SOUTH;
+                        break;
+                    case BOTTOM_RIGHT:
+                        cursor = Cursor.RESIZE_NORTH;
+                        break;
+                    }
+                    break;
+                }
+            }
+
+            setCursor(cursor);
+        }
+    }
+
+    /**
+     * Split pane splitter component skin.
+     *
+     * @author tvolkert
+     */
+    protected class SplitterSkin extends ComponentSkin {
+        private class DragHandler
+            implements ComponentMouseListener, ComponentMouseButtonListener {
+            public boolean mouseMove(Component component, int x, int y) {
+                SplitPane splitPane = (SplitPane)TerraSplitPaneSkin.this.getComponent();
+
+                Orientation orientation = splitPane.getOrientation();
+
+                // Calculate the would-be new split location
+                int splitLocation;
+                if (orientation == Orientation.HORIZONTAL) {
+                    splitLocation = x - dragOffset;
+                } else {
+                    splitLocation = y - dragOffset;
+                }
+
+                splitLocation = boundSplitLocation(splitLocation);
+
+                if (shadow == null) {
+                    // Update the split location immediately
+                    splitPane.setSplitLocation(splitLocation);
+                } else {
+                    // Move the shadow
+                    if (orientation == Orientation.HORIZONTAL) {
+                        shadow.setLocation(splitLocation, 0);
+                    } else {
+                        shadow.setLocation(0, splitLocation);
+                    }
+                }
+
+                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 (shadow != null) {
+                    SplitPane splitPane = (SplitPane)TerraSplitPaneSkin.this.getComponent();
+
+                    // Update the split location and remove the shadow
+                    int splitLocation;
+                    if (splitPane.getOrientation() == Orientation.HORIZONTAL) {
+                        splitLocation = shadow.getX();
+                    } else {
+                        splitLocation = shadow.getY();
+                    }
+
+                    splitPane.setSplitLocation(splitLocation);
+
+                    splitPane.remove(shadow);
+                    shadow = null;
+                }
+
+                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 int dragOffset;
+        private SplitterShadow shadow = null;
+        private DragHandler dragHandler = new DragHandler();
+
+        @Override
+        public boolean isFocusable() {
+            return false;
+        }
+
+        public int getPreferredWidth(int height) {
+            // This will never get called since the size of the splitter is set
+            // automatically by SplitPaneSkin using the the size of the
+            // SplitPane and the split thickness
+            return 0;
+        }
+
+        public int getPreferredHeight(int width) {
+            // This will never get called since the size of the splitter is set
+            // automatically by SplitPaneSkin using the the size of the
+            // SplitPane and the split thickness
+            return 0;
+        }
+
+        public void layout() {
+            // No-op
+        }
+
+        public void paint(Graphics2D graphics) {
+            SplitPane splitPane = (SplitPane)TerraSplitPaneSkin.this.getComponent();
+
+            Orientation orientation = splitPane.getOrientation();
+
+            int width = getWidth();
+            int height = getHeight();
+
+            int imageWidth, imageHeight;
+            if (orientation == Orientation.HORIZONTAL) {
+                imageWidth = width - 4;
+                imageHeight = Math.min(height - 4, 8);
+            } else {
+                imageWidth = Math.min(width - 4, 8);
+                imageHeight = height - 4;
+            }
+
+            if (imageWidth > 0 && imageHeight > 0) {
+                int translateX = (width - imageWidth) / 2;
+                int translateY = (height - imageHeight) / 2;
+                graphics.translate(translateX, translateY);
+
+                Color dark = splitterHandlePrimaryColor;
+                Color light = splitterHandleSecondaryColor;
+
+                if (orientation == Orientation.HORIZONTAL) {
+                    graphics.setStroke(new BasicStroke());
+
+                    graphics.setPaint(dark);
+                    graphics.drawLine(0, 0, imageWidth - 1, 0);
+                    graphics.drawLine(0, 3, imageWidth - 1, 3);
+                    graphics.drawLine(0, 6, imageWidth - 1, 6);
+
+                    graphics.setPaint(light);
+                    graphics.drawLine(0, 1, imageWidth - 1, 1);
+                    graphics.drawLine(0, 4, imageWidth - 1, 4);
+                    graphics.drawLine(0, 7, imageWidth - 1, 7);
+                } else {
+                    int half = imageHeight / 2;
+
+                    graphics.setPaint(dark);
+                    graphics.fillRect(0, 0, 2, half);
+                    graphics.fillRect(3, 0, 2, half);
+                    graphics.fillRect(6, 0, 2, half);
+
+                    graphics.setPaint(light);
+                    graphics.fillRect(0, half, 2, half);
+                    graphics.fillRect(3, half, 2, half);
+                    graphics.fillRect(6, half, 2, half);
+                }
+            }
+        }
+
+        @Override
+        public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+            Splitter splitter = (Splitter)getComponent();
+            SplitPane splitPane = (SplitPane)TerraSplitPaneSkin.this.getComponent();
+
+            Orientation orientation = splitPane.getOrientation();
+
+            if (!splitPane.isLocked()) {
+                int splitLocation = splitPane.getSplitLocation();
+
+                Display display = splitPane.getDisplay();
+                Point displayCoordinates = splitter.mapPointToAncestor(display, x, y);
+
+                if (orientation == Orientation.HORIZONTAL) {
+                    dragOffset = displayCoordinates.x - splitLocation;
+                } else {
+                    dragOffset = displayCoordinates.y - splitLocation;
+                }
+
+                if (useShadow) {
+                    shadow = new SplitterShadow();
+                    splitPane.add(shadow);
+
+                    if (orientation == Orientation.HORIZONTAL) {
+                        shadow.setLocation(splitter.getX(), 0);
+                    } else {
+                        shadow.setLocation(0, splitter.getY());
+                    }
+                    shadow.setSize(getWidth(), getHeight());
+                }
+
+                display.getComponentMouseListeners().add(dragHandler);
+                display.getComponentMouseButtonListeners().add(dragHandler);
+            }
+
+            return false;
+        }
+
+        private int boundSplitLocation(int splitLocation) {
+            SplitPane splitPane = (SplitPane)TerraSplitPaneSkin.this.getComponent();
+
+            int lower = 0;
+            int upper;
+            if (splitPane.getOrientation() == Orientation.HORIZONTAL) {
+                upper = splitPane.getWidth() - splitterThickness;
+            } else {
+                upper = splitPane.getHeight() - splitterThickness;
+            }
+
+            Span bounds = splitPane.getSplitBounds();
+            if (bounds != null) {
+                lower = Math.max(lower, bounds.getStart());
+                upper = Math.min(upper, bounds.getEnd());
+            }
+
+            if (splitLocation < lower) {
+                splitLocation = lower;
+            } else if (splitLocation > upper) {
+                splitLocation = upper;
+            }
+
+            return splitLocation;
+        }
+    }
+
+    /**
+     * Split pane splitter shadow component.
+     *
+     * @author tvolkert
+     */
+    protected class SplitterShadow extends Component {
+        public SplitterShadow() {
+            super();
+            setSkin(new SplitterShadowSkin());
+        }
+    }
+
+    /**
+     * Split pane splitter shadow component skin.
+     *
+     * @author tvolkert
+     */
+    protected class SplitterShadowSkin extends ComponentSkin {
+        public int getPreferredWidth(int height) {
+            // This will never get called since the splitter will always just
+            // set the size of its shadow to match its own size
+            return 0;
+        }
+
+        public int getPreferredHeight(int width) {
+            // This will never get called since the splitter will always just
+            // set the size of its shadow to match its own size
+            return 0;
+        }
+
+        public void layout() {
+            // No-op
+        }
+
+        public void paint(Graphics2D graphics) {
+            graphics.setStroke(new BasicStroke());
+            graphics.setPaint(new Color(0, 0, 0, 64));
+            graphics.fillRect(0, 0, getWidth(), getHeight());
+        }
+    }
+
+    private Splitter splitter = new Splitter();
+
+    private Color splitterHandlePrimaryColor;
+    private Color splitterHandleSecondaryColor;
+    private int splitterThickness;
+    private boolean useShadow;
+
+    public TerraSplitPaneSkin() {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        splitterHandlePrimaryColor = theme.getColor(9);
+        splitterHandleSecondaryColor = theme.getColor(10);
+        splitterThickness = 6;
+        useShadow = false;
+    }
+
+    @Override
+    public void install(Component component) {
+        super.install(component);
+
+        SplitPane splitPane = (SplitPane)component;
+        splitPane.getSplitPaneListeners().add(this);
+
+        splitPane.add(splitter);
+        splitter.updateCursor();
+    }
+
+    @Override
+    public void uninstall() {
+        SplitPane splitPane = (SplitPane)getComponent();
+        splitPane.getSplitPaneListeners().remove(this);
+
+        splitPane.remove(splitter);
+
+        super.uninstall();
+    }
+
+    @Override
+    public int getPreferredWidth(int height) {
+        return 0;
+    }
+
+    @Override
+    public int getPreferredHeight(int width) {
+        return 0;
+    }
+
+    @Override
+    public Dimensions getPreferredSize() {
+        return new Dimensions(0, 0);
+    }
+
+    public void layout() {
+        int width = getWidth();
+        int height = getHeight();
+
+        SplitPane splitPane = (SplitPane)getComponent();
+        int splitLocation = splitPane.getSplitLocation();
+        Component leftComponent = splitPane.getTopLeftComponent();
+        Component rightComponent = splitPane.getBottomRightComponent();
+
+        int rightStart = splitLocation + splitterThickness;
+
+        if (splitPane.getOrientation() == Orientation.HORIZONTAL) {
+            splitter.setLocation(splitLocation, 0);
+            splitter.setSize(splitterThickness, height);
+
+            if (leftComponent != null) {
+                leftComponent.setLocation(0, 0);
+                leftComponent.setSize(splitLocation, height);
+            }
+
+            if (rightComponent != null) {
+                rightComponent.setLocation(rightStart, 0);
+                rightComponent.setSize(Math.max(width - rightStart, 0), height);
+            }
+        } else {
+            splitter.setLocation(0, splitLocation);
+            splitter.setSize(width, splitterThickness);
+
+            if (leftComponent != null) {
+                leftComponent.setLocation(0, 0);
+                leftComponent.setSize(width, splitLocation);
+            }
+
+            if (rightComponent != null) {
+                rightComponent.setLocation(0, rightStart);
+                rightComponent.setSize(width, Math.max(height - rightStart, 0));
+            }
+        }
+    }
+
+    public Color getSplitterHandlePrimaryColor() {
+        return splitterHandlePrimaryColor;
+    }
+
+    public void setSplitterHandlePrimaryColor(Color splitterHandlePrimaryColor) {
+        if (splitterHandlePrimaryColor == null) {
+            throw new IllegalArgumentException("splitterHandlePrimaryColor is null.");
+        }
+
+        this.splitterHandlePrimaryColor = splitterHandlePrimaryColor;
+        splitter.repaint();
+    }
+
+    public final void setSplitterHandlePrimaryColor(String splitterHandlePrimaryColor) {
+        if (splitterHandlePrimaryColor == null) {
+            throw new IllegalArgumentException("splitterHandlePrimaryColor is null.");
+        }
+
+        setSplitterHandlePrimaryColor(decodeColor(splitterHandlePrimaryColor));
+    }
+
+    public Color getSplitterHandleSecondaryColor() {
+        return splitterHandleSecondaryColor;
+    }
+
+    public void setSplitterHandleSecondaryColor(Color splitterHandleSecondaryColor) {
+        if (splitterHandleSecondaryColor == null) {
+            throw new IllegalArgumentException("splitterHandleSecondaryColor is null.");
+        }
+
+        this.splitterHandleSecondaryColor = splitterHandleSecondaryColor;
+        splitter.repaint();
+    }
+
+    public final void setSplitterHandleSecondaryColor(String splitterHandleSecondaryColor) {
+        if (splitterHandleSecondaryColor == null) {
+            throw new IllegalArgumentException("splitterHandleSecondaryColor is null.");
+        }
+
+        setSplitterHandleSecondaryColor(decodeColor(splitterHandleSecondaryColor));
+    }
+
+    public int getSplitterThickness() {
+        return splitterThickness;
+    }
+
+    public void setSplitterThickness(int splitterThickness) {
+        this.splitterThickness = splitterThickness;
+        invalidateComponent();
+    }
+
+    public final void setSplitterThickness(String splitterThickness) {
+        if (splitterThickness == null) {
+            throw new IllegalArgumentException("splitterThickness is null.");
+        }
+
+        setSplitterThickness(Integer.parseInt(splitterThickness));
+    }
+
+    public boolean getUseShadow() {
+        return useShadow;
+    }
+
+    public void setUseShadow(boolean useShadow) {
+        this.useShadow = useShadow;
+    }
+
+    public void topLeftComponentChanged(SplitPane splitPane, Component previousTopLeftComponent) {
+        // No-op
+    }
+
+    public void bottomRightComponentChanged(SplitPane splitPane, Component previousBottomRightComponent) {
+        // No-op
+    }
+
+    public void orientationChanged(SplitPane splitPane) {
+        splitter.updateCursor();
+        invalidateComponent();
+    }
+
+    public void primaryRegionChanged(SplitPane splitPane) {
+        splitter.updateCursor();
+    }
+
+    public void splitLocationChanged(SplitPane splitPane, int previousSplitLocation) {
+        invalidateComponent();
+    }
+
+    public void splitBoundsChanged(SplitPane splitPane, Span previousSplitBounds) {
+        invalidateComponent();
+    }
+
+    public void lockedChanged(SplitPane splitPane) {
+        splitter.updateCursor();
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,990 @@
+/*
+ * 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.Font;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+
+import pivot.collections.Dictionary;
+import pivot.collections.Sequence;
+import pivot.wtk.Button;
+import pivot.wtk.Component;
+import pivot.wtk.Dimensions;
+import pivot.wtk.FlowPane;
+import pivot.wtk.HorizontalAlignment;
+import pivot.wtk.Insets;
+import pivot.wtk.Mouse;
+import pivot.wtk.Orientation;
+import pivot.wtk.Panorama;
+import pivot.wtk.Bounds;
+import pivot.wtk.TabPane;
+import pivot.wtk.TabPaneListener;
+import pivot.wtk.TabPaneSelectionListener;
+import pivot.wtk.TabPaneAttributeListener;
+import pivot.wtk.Theme;
+import pivot.wtk.VerticalAlignment;
+import pivot.wtk.Button.Group;
+import pivot.wtk.content.ButtonData;
+import pivot.wtk.content.ButtonDataRenderer;
+import pivot.wtk.media.Image;
+import pivot.wtk.skin.ButtonSkin;
+import pivot.wtk.skin.ContainerSkin;
+
+/**
+ * Tab pane skin.
+ * <p>
+ * TODO Make tab buttons focusable?
+ * <p>
+ * TODO Disable the tab button when the component is disabled? We'd need
+ * style properties to present a disabled tab button state. We'd also need
+ * to manage button enabled state independently of tab pane enabled state.
+ * <p>
+ * TODO Support the displayable flag to show/hide tabs.
+ * <p>
+ * TODO Add showCloseButton style.
+ *
+ * @author gbrown
+ */
+public class TerraTabPaneSkin extends ContainerSkin
+    implements TabPaneListener, TabPaneSelectionListener, TabPaneAttributeListener,
+        Button.GroupListener {
+    protected class TabButton extends Button {
+        public TabButton() {
+            this(null);
+        }
+
+        public TabButton(Object buttonData) {
+            super(buttonData);
+
+            super.setToggleButton(true);
+            setDataRenderer(DEFAULT_DATA_RENDERER);
+
+            setSkin(new TabButtonSkin());
+        }
+
+        @Override
+        public boolean isEnabled() {
+            TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+            return tabPane.isEnabled();
+        }
+
+        @Override
+        public void setEnabled(boolean enabled) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setToggleButton(boolean toggleButton) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setTriState(boolean triState) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void press() {
+            // If the tab pane is collapsible, toggle the button selection;
+            // otherwise, select it
+            TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+            setSelected(tabPane.isCollapsible() ? !isSelected() : true);
+
+            super.press();
+        }
+    }
+
+    protected class TabButtonSkin extends ButtonSkin {
+        public int getPreferredWidth(int height) {
+            TabButton tabButton = (TabButton)getComponent();
+            TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+
+            Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
+            dataRenderer.render(tabButton.getButtonData(), tabButton, false);
+
+            // Include padding in constraint
+            if (height != -1) {
+                height = Math.max(height - (buttonPadding.top + buttonPadding.bottom + 2), 0);
+            }
+
+            int preferredWidth = 0;
+            switch (tabPane.getTabOrientation()) {
+                case HORIZONTAL: {
+                    preferredWidth = dataRenderer.getPreferredWidth(height)
+                        + buttonPadding.left + buttonPadding.right + 2;
+                    break;
+                }
+
+                case VERTICAL: {
+                    preferredWidth = dataRenderer.getPreferredHeight(height)
+                        + buttonPadding.top + buttonPadding.bottom + 2;
+                    break;
+                }
+            }
+
+            return preferredWidth;
+        }
+
+        public int getPreferredHeight(int width) {
+            TabButton tabButton = (TabButton)getComponent();
+            TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+
+            Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
+            dataRenderer.render(tabButton.getButtonData(), tabButton, false);
+
+            // Include padding in constraint
+            if (width != -1) {
+                width = Math.max(width - (buttonPadding.left + buttonPadding.right + 2), 0);
+            }
+
+            int preferredHeight = 0;
+            switch (tabPane.getTabOrientation()) {
+                case HORIZONTAL: {
+                    preferredHeight = dataRenderer.getPreferredHeight(width)
+                        + buttonPadding.top + buttonPadding.bottom + 2;
+                    break;
+                }
+
+                case VERTICAL: {
+                    preferredHeight = dataRenderer.getPreferredWidth(width)
+                        + buttonPadding.left + buttonPadding.right + 2;
+                    break;
+                }
+            }
+
+            return preferredHeight;
+        }
+
+        public Dimensions getPreferredSize() {
+            TabButton tabButton = (TabButton)getComponent();
+            TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+
+            Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
+            dataRenderer.render(tabButton.getButtonData(), tabButton, false);
+
+            Dimensions preferredContentSize = dataRenderer.getPreferredSize();
+
+            int preferredWidth = 0;
+            int preferredHeight = 0;
+            switch (tabPane.getTabOrientation()) {
+                case HORIZONTAL: {
+                    preferredWidth = preferredContentSize.width
+                        + buttonPadding.left + buttonPadding.right + 2;
+
+                    preferredHeight = preferredContentSize.height
+                        + buttonPadding.top + buttonPadding.bottom + 2;
+
+                    break;
+                }
+
+                case VERTICAL: {
+                    preferredWidth = preferredContentSize.height
+                        + buttonPadding.top + buttonPadding.bottom + 2;
+
+                    preferredHeight = preferredContentSize.width
+                        + buttonPadding.left + buttonPadding.right + 2;
+
+                    break;
+                }
+            }
+
+            return new Dimensions(preferredWidth, preferredHeight);
+        }
+
+        public void paint(Graphics2D graphics) {
+            TabButton tabButton = (TabButton)getComponent();
+            TabPane tabPane = (TabPane)TerraTabPaneSkin.this.getComponent();
+            Orientation tabOrientation = tabPane.getTabOrientation();
+
+            Color backgroundColor = (tabButton.isSelected()) ?
+                activeTabColor : inactiveTabColor;
+
+            int width = getWidth();
+            int height = getHeight();
+
+            graphics.setStroke(new BasicStroke());
+
+            // Draw the background
+            graphics.setPaint(backgroundColor);
+            graphics.fillRect(0, 0, width, height);
+
+            // Draw the bevel
+            graphics.setPaint(new GradientPaint(width / 2, 1, buttonBevelColor,
+                width / 2, GRADIENT_BEVEL_THICKNESS, backgroundColor));
+
+            switch(tabOrientation) {
+	            case HORIZONTAL: {
+	                graphics.fillRect(1, 1, width - 2, GRADIENT_BEVEL_THICKNESS);
+	                break;
+	            }
+
+	            case VERTICAL: {
+	            	graphics.fillRect(1, 1, width - 1, GRADIENT_BEVEL_THICKNESS);
+	                break;
+	            }
+	        }
+
+            // Draw the border
+            graphics.setPaint(borderColor);
+
+            if (tabButton.isSelected()) {
+                switch(tabOrientation) {
+		            case HORIZONTAL: {
+		                graphics.drawLine(0, height - 1, 0, 0);
+		                graphics.drawLine(0, 0, width - 1, 0);
+		                graphics.drawLine(width - 1, 0, width - 1, height - 1);
+		                break;
+		            }
+
+		            case VERTICAL: {
+		                graphics.drawLine(width, 0, 0, 0);
+		                graphics.drawLine(0, 0, 0, height - 1);
+		                graphics.drawLine(0, height - 1, width - 1, height - 1);
+		                break;
+		            }
+	            }
+            } else {
+            	graphics.drawRect(0, 0, width - 1, height - 1);
+            }
+
+            // Paint the content
+            Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
+            dataRenderer.render(tabButton.getButtonData(), tabButton, false);
+
+            Graphics2D contentGraphics = (Graphics2D)graphics.create();
+
+            switch (tabOrientation) {
+                case HORIZONTAL: {
+                    dataRenderer.setSize(Math.max(getWidth() - (buttonPadding.left + buttonPadding.right + 2), 0),
+                        Math.max(getHeight() - (buttonPadding.top + buttonPadding.bottom + 2), 0));
+
+                    contentGraphics.translate(buttonPadding.left + 1, buttonPadding.top + 1);
+                    break;
+                }
+
+                case VERTICAL: {
+                    dataRenderer.setSize(Math.max(getHeight() - (buttonPadding.top + buttonPadding.bottom + 2), 0),
+                        Math.max(getWidth() - (buttonPadding.left + buttonPadding.right + 2), 0));
+
+                    contentGraphics.translate(buttonPadding.top + 1, buttonPadding.left + 1);
+                    contentGraphics.rotate(-Math.PI / 2d);
+                    contentGraphics.translate(-dataRenderer.getWidth(), 0);
+                    break;
+                }
+            }
+
+            contentGraphics.clipRect(0, 0, dataRenderer.getWidth(), dataRenderer.getHeight());
+            dataRenderer.paint(contentGraphics);
+
+            contentGraphics.dispose();
+        }
+
+        @Override
+        public boolean isFocusable() {
+            return false;
+        }
+
+        @Override
+        public void mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+            TabButton tabButton = (TabButton)getComponent();
+            tabButton.press();
+        }
+
+        public Font getFont() {
+        	return buttonFont;
+        }
+
+        public Color getColor() {
+        	return buttonColor;
+        }
+
+        public Color getDisabledColor() {
+        	return buttonColor;
+        }
+    }
+
+    protected Panorama buttonPanorama = new Panorama();
+    protected FlowPane buttonFlowPane = new FlowPane();
+    private Button.Group tabButtonGroup = new Button.Group();
+
+    private Color activeTabColor;
+    private Color inactiveTabColor;
+    private Color borderColor;
+    private Insets padding;
+    private Font buttonFont;
+    private Color buttonColor;
+    private Insets buttonPadding;
+
+    // Derived colors
+    private Color buttonBevelColor;
+
+	public static final int GRADIENT_BEVEL_THICKNESS = 4;
+	private static final Button.DataRenderer DEFAULT_DATA_RENDERER = new ButtonDataRenderer();
+
+    public TerraTabPaneSkin() {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        activeTabColor = theme.getColor(11);
+        inactiveTabColor = theme.getColor(9);
+        borderColor = theme.getColor(7);
+        padding = new Insets(6);
+        buttonFont = theme.getFont();
+        buttonColor = theme.getColor(1);
+        buttonPadding = new Insets(3, 4, 3, 4);
+
+        // Set the derived colors
+        buttonBevelColor = TerraTheme.brighten(inactiveTabColor);
+
+        tabButtonGroup.getGroupListeners().add(this);
+
+        buttonFlowPane.getStyles().put("spacing", 2);
+    }
+
+    public void install(Component component) {
+        super.install(component);
+
+        TabPane tabPane = (TabPane)component;
+
+        // Add this as a listener on the tab pane
+        tabPane.getTabPaneListeners().add(this);
+        tabPane.getTabPaneSelectionListeners().add(this);
+        tabPane.getTabPaneAttributeListeners().add(this);
+
+        // Add the button panorama and flow pane
+        buttonPanorama.getStyles().put("buttonBackgroundColor", borderColor);
+        buttonPanorama.getStyles().put("buttonPadding", 6);
+        buttonPanorama.setView(buttonFlowPane);
+        tabPane.add(buttonPanorama);
+
+        // Apply the current tab orientation
+        tabOrientationChanged(tabPane);
+
+        // Add buttons for all existing tabs
+        for (Component tab : tabPane.getTabs()) {
+            TabButton tabButton = new TabButton(new ButtonData(TabPane.getIcon(tab),
+                TabPane.getName(tab)));
+            tabButton.setGroup(tabButtonGroup);
+
+            buttonFlowPane.add(tabButton);
+        }
+
+        selectedIndexChanged(tabPane, -1);
+    }
+
+    public void uninstall() {
+        TabPane tabPane = (TabPane)getComponent();
+
+        // Remove this as a listener on the tab pane
+        tabPane.getTabPaneListeners().remove(this);
+        tabPane.getTabPaneSelectionListeners().remove(this);
+        tabPane.getTabPaneAttributeListeners().remove(this);
+
+        // Remove the button panorama
+        tabPane.remove(buttonPanorama);
+
+        super.uninstall();
+    }
+
+    public int getPreferredWidth(int height) {
+        int preferredWidth = 0;
+
+        TabPane tabPane = (TabPane)getComponent();
+        Orientation tabOrientation = tabPane.getTabOrientation();
+        Component corner = tabPane.getCorner();
+
+        switch (tabOrientation) {
+            case HORIZONTAL: {
+                if (height != -1) {
+                    if (corner != null
+                        && corner.isDisplayable()) {
+                        height = Math.max(height - Math.max(corner.getPreferredHeight(-1),
+                            Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0)), 0);
+                    } else {
+                        height = Math.max(height - (buttonPanorama.getPreferredHeight(-1) - 1), 0);
+                    }
+
+                    height = Math.max(height - (padding.top + padding.bottom + 2), 0);
+                }
+
+                if (tabPane.getSelectedIndex() != -1) {
+                    for (Component tab : tabPane.getTabs()) {
+                        preferredWidth = Math.max(preferredWidth,
+                            tab.getPreferredWidth(height));
+                    }
+
+                    preferredWidth += (padding.left + padding.right + 2);
+                }
+
+                int buttonAreaPreferredWidth = buttonPanorama.getPreferredWidth(-1);
+
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    buttonAreaPreferredWidth += corner.getPreferredWidth(-1);
+                }
+
+                preferredWidth = Math.max(preferredWidth,
+                    buttonAreaPreferredWidth);
+
+                break;
+            }
+
+            case VERTICAL: {
+                if (height != -1) {
+                    height = Math.max(height - (padding.top + padding.bottom + 2), 0);
+                }
+
+                if (tabPane.getSelectedIndex() != -1) {
+                    for (Component tab : tabPane.getTabs()) {
+                        preferredWidth = Math.max(preferredWidth,
+                            tab.getPreferredWidth(height));
+                    }
+
+                    preferredWidth += (padding.left + padding.right + 2);
+                } else {
+                    preferredWidth += 1;
+                }
+
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    preferredWidth += Math.max(corner.getPreferredWidth(-1),
+                        Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0));
+                } else {
+                    preferredWidth += Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0);
+                }
+
+                break;
+            }
+        }
+
+        return preferredWidth;
+    }
+
+    public int getPreferredHeight(int width) {
+        int preferredHeight = 0;
+
+        TabPane tabPane = (TabPane)getComponent();
+        Orientation tabOrientation = tabPane.getTabOrientation();
+        Component corner = tabPane.getCorner();
+
+        switch (tabOrientation) {
+            case HORIZONTAL: {
+                if (width != -1) {
+                    width = Math.max(width - (padding.left + padding.right + 2), 0);
+                }
+
+                if (tabPane.getSelectedIndex() != -1) {
+                    for (Component tab : tabPane.getTabs()) {
+                        preferredHeight = Math.max(preferredHeight,
+                            tab.getPreferredHeight(width));
+                    }
+
+                    preferredHeight += (padding.top + padding.bottom + 2);
+                } else {
+                    preferredHeight += 1;
+                }
+
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    preferredHeight += Math.max(corner.getPreferredHeight(-1),
+                        Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0));
+                } else {
+                    preferredHeight += Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0);
+                }
+
+                break;
+            }
+
+            case VERTICAL: {
+                if (width != -1) {
+                    if (corner != null
+                        && corner.isDisplayable()) {
+                        width = Math.max(width - Math.max(corner.getPreferredWidth(-1),
+                            Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0)), 0);
+                    } else {
+                        width = Math.max(width - (buttonPanorama.getPreferredWidth(-1) - 1), 0);
+                    }
+
+                    width = Math.max(width - (padding.left + padding.right + 2), 0);
+                }
+
+                if (tabPane.getSelectedIndex() != -1) {
+                    for (Component tab : tabPane.getTabs()) {
+                        preferredHeight = Math.max(preferredHeight,
+                            tab.getPreferredHeight(width));
+                    }
+
+                    preferredHeight += (padding.top + padding.bottom + 2);
+                }
+
+                int buttonAreaPreferredHeight = buttonPanorama.getPreferredHeight(-1);
+
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    buttonAreaPreferredHeight += corner.getPreferredHeight(-1);
+                }
+
+                preferredHeight = Math.max(preferredHeight,
+                    buttonAreaPreferredHeight);
+
+                break;
+            }
+        }
+
+        return preferredHeight;
+    }
+
+    public Dimensions getPreferredSize() {
+        // TODO Optimize
+        return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+    }
+
+    public void layout() {
+        TabPane tabPane = (TabPane)getComponent();
+        Component corner = tabPane.getCorner();
+
+        int width = getWidth();
+        int height = getHeight();
+
+        int tabX = 0;
+        int tabY = 0;
+        int tabWidth = 0;
+        int tabHeight = 0;
+
+        Dimensions buttonPanoramaPreferredSize = buttonPanorama.getPreferredSize();
+
+        switch (tabPane.getTabOrientation()) {
+            case HORIZONTAL: {
+                int buttonPanoramaWidth = Math.min(width,
+                    buttonPanoramaPreferredSize.width);
+                int buttonPanoramaHeight = buttonPanoramaPreferredSize.height;
+                int buttonPanoramaX = 0;
+                int buttonPanoramaY = 0;
+
+                if (corner != null) {
+                    if (corner.isDisplayable()) {
+                        int cornerWidth = width - buttonPanoramaWidth;
+                        int cornerHeight = Math.max(corner.getPreferredHeight(-1), buttonPanoramaPreferredSize.height - 1);
+                        int cornerX = buttonPanoramaWidth;
+                        int cornerY = Math.max(buttonPanoramaHeight - cornerHeight - 1, 0);
+
+                        buttonPanoramaY = Math.max(cornerHeight - buttonPanoramaHeight + 1, 0);
+
+                        corner.setVisible(true);
+                        corner.setLocation(cornerX, cornerY);
+                        corner.setSize(cornerWidth, cornerHeight);
+                    } else {
+                        corner.setVisible(false);
+                    }
+                }
+
+                buttonPanorama.setLocation(buttonPanoramaX, buttonPanoramaY);
+                buttonPanorama.setSize(buttonPanoramaWidth, buttonPanoramaHeight);
+
+                tabX = padding.left + 1;
+                tabY = padding.top + buttonPanoramaY + buttonPanoramaHeight;
+                tabWidth = Math.max(width - (padding.left + padding.right + 2), 0);
+                tabHeight = Math.max(height - (padding.top + padding.bottom
+                    + buttonPanoramaY + buttonPanoramaHeight + 1), 0);
+
+                break;
+            }
+
+            case VERTICAL: {
+                int buttonPanoramaWidth = buttonPanoramaPreferredSize.width;
+                int buttonPanoramaHeight = Math.min(height,
+                    buttonPanoramaPreferredSize.height);
+                int buttonPanoramaX = 0;
+                int buttonPanoramaY = 0;
+
+                if (corner != null) {
+                    if (corner.isDisplayable()) {
+                        int cornerWidth = corner.getPreferredWidth(-1);
+                        int cornerHeight = height - buttonPanoramaHeight;
+                        int cornerX = Math.max(buttonPanoramaWidth - cornerWidth - 1, 0);
+                        int cornerY = buttonPanoramaHeight;
+
+                        buttonPanoramaX = Math.max(cornerWidth - buttonPanoramaWidth + 1, 0);
+
+                        corner.setVisible(true);
+                        corner.setLocation(cornerX, cornerY);
+                        corner.setSize(cornerWidth, cornerHeight);
+                    } else {
+                        corner.setVisible(false);
+                    }
+                }
+
+                buttonPanorama.setLocation(buttonPanoramaX, buttonPanoramaY);
+                buttonPanorama.setSize(buttonPanoramaWidth, buttonPanoramaHeight);
+
+                tabX = padding.left + buttonPanoramaX + buttonPanoramaWidth;
+                tabY = padding.top + 1;
+                tabWidth = Math.max(width - (padding.left + padding.right
+                    + buttonPanoramaX + buttonPanoramaWidth + 1), 0);
+                tabHeight = Math.max(height - (padding.top + padding.bottom + 2), 0);
+
+                break;
+            }
+        }
+
+        TabPane.TabSequence tabs = tabPane.getTabs();
+        int selectedIndex = tabPane.getSelectedIndex();
+
+        for (int i = 0, n = tabs.getLength(); i < n; i++) {
+            Component tab = tabs.get(i);
+            if (i == selectedIndex) {
+                // Show the selected tab
+                tab.setVisible(true);
+
+                // Set the tab's size and location
+                tab.setLocation(tabX, tabY);
+                tab.setSize(tabWidth, tabHeight);
+            } else {
+                tab.setVisible(false);
+            }
+        }
+    }
+
+    @Override
+    public void paint(Graphics2D graphics) {
+        TabPane tabPane = (TabPane)getComponent();
+
+        Bounds tabPaneBounds = tabPane.getBounds();
+
+        // Call the base class to paint the background
+        super.paint(graphics);
+
+        graphics.setStroke(new BasicStroke());
+
+        // Paint the content background and border
+        int x = 0;
+        int y = 0;
+        int width = 0;
+        int height = 0;
+
+        Orientation tabOrientation = tabPane.getTabOrientation();
+
+        switch (tabOrientation) {
+            case HORIZONTAL: {
+                x = 0;
+                y = Math.max(buttonPanorama.getY() + buttonPanorama.getHeight() - 1, 0);
+                width = tabPaneBounds.width;
+                height = Math.max(tabPaneBounds.height - (y + 1), 0);
+
+                break;
+            }
+
+            case VERTICAL: {
+                x = Math.max(buttonPanorama.getX() + buttonPanorama.getWidth() - 1, 0);
+                y = 0;
+                width = Math.max(tabPaneBounds.width - (x + 1), 0);
+                height = tabPaneBounds.height;
+
+                break;
+            }
+        }
+
+        Bounds contentBounds = new Bounds(x, y, width, height);
+
+        if (!contentBounds.isEmpty()) {
+            // If a tab is selected, paint the active background color; otherwise,
+            // paint the inactive background color
+            int selectedIndex = tabPane.getSelectedIndex();
+            graphics.setPaint((selectedIndex == -1) ? inactiveTabColor : activeTabColor);
+            graphics.fillRect(contentBounds.x, contentBounds.y,
+                contentBounds.width, contentBounds.height);
+
+            // Draw the border
+            graphics.setPaint(borderColor);
+            graphics.drawRect(contentBounds.x, contentBounds.y,
+                Math.max(contentBounds.width - 1, 0),
+                Math.max(contentBounds.height - 1, 0));
+
+            // Draw the bevel for vertical tabs
+            if (tabOrientation == Orientation.VERTICAL) {
+	            graphics.setPaint(new GradientPaint(width / 2, contentBounds.y + 1, buttonBevelColor,
+	                width / 2, contentBounds.y + 1 + GRADIENT_BEVEL_THICKNESS, activeTabColor));
+	            graphics.fillRect(contentBounds.x + 1, contentBounds.y + 1,
+                    contentBounds.width - 2, GRADIENT_BEVEL_THICKNESS);
+            }
+        }
+    }
+
+    public Color getActiveTabColor() {
+        return activeTabColor;
+    }
+
+    public void setActiveTabColor(Color activeTabColor) {
+        if (activeTabColor == null) {
+            throw new IllegalArgumentException("activeTabColor is null.");
+        }
+
+        this.activeTabColor = activeTabColor;
+        repaintComponent();
+    }
+
+    public final void setActiveTabColor(String activeTabColor) {
+        if (activeTabColor == null) {
+            throw new IllegalArgumentException("activeTabColor is null.");
+        }
+
+        setActiveTabColor(decodeColor(activeTabColor));
+    }
+
+    public Color getInactiveTabColor() {
+        return inactiveTabColor;
+    }
+
+    public void setInactiveTabColor(Color inactiveTabColor) {
+        if (inactiveTabColor == null) {
+            throw new IllegalArgumentException("inactiveTabColor is null.");
+        }
+
+        this.inactiveTabColor = inactiveTabColor;
+        buttonBevelColor = TerraTheme.brighten(inactiveTabColor);
+        repaintComponent();
+    }
+
+    public final void setInactiveTabColor(String inactiveTabColor) {
+        if (inactiveTabColor == null) {
+            throw new IllegalArgumentException("inactiveTabColor is null.");
+        }
+
+        setInactiveTabColor(decodeColor(inactiveTabColor));
+    }
+
+    public Color getBorderColor() {
+        return borderColor;
+    }
+
+    public void setBorderColor(Color borderColor) {
+        if (borderColor == null) {
+            throw new IllegalArgumentException("borderColor is null.");
+        }
+
+        this.borderColor = borderColor;
+        buttonPanorama.getStyles().put("buttonBackgroundColor", 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 Font getButtonFont() {
+        return buttonFont;
+    }
+
+    public void setButtonFont(Font buttonFont) {
+        if (buttonFont == null) {
+            throw new IllegalArgumentException("buttonFont is null.");
+        }
+
+        this.buttonFont = buttonFont;
+        invalidateComponent();
+    }
+
+    public final void setButtonFont(String buttonFont) {
+        if (buttonFont == null) {
+            throw new IllegalArgumentException("buttonFont is null.");
+        }
+
+        setButtonFont(Font.decode(buttonFont));
+    }
+
+    public Color getButtonColor() {
+        return buttonColor;
+    }
+
+    public void setButtonColor(Color buttonColor) {
+        if (buttonColor == null) {
+            throw new IllegalArgumentException("buttonColor is null.");
+        }
+
+        this.buttonColor = buttonColor;
+        repaintComponent();
+    }
+
+    public final void setButtonColor(String buttonColor) {
+        if (buttonColor == null) {
+            throw new IllegalArgumentException("buttonColor is null.");
+        }
+
+        setButtonColor(decodeColor(buttonColor));
+    }
+
+    public Insets getButtonPadding() {
+        return buttonPadding;
+    }
+
+    public void setButtonPadding(Insets buttonPadding) {
+        if (buttonPadding == null) {
+            throw new IllegalArgumentException("buttonPadding is null.");
+        }
+
+        this.buttonPadding = buttonPadding;
+        invalidateComponent();
+    }
+
+    public final void setButtonPadding(int buttonPadding) {
+        setButtonPadding(new Insets(buttonPadding));
+    }
+
+    public int getButtonSpacing() {
+        return (Integer)buttonFlowPane.getStyles().get("spacing");
+    }
+
+    public void setButtonSpacing(int buttonSpacing) {
+        buttonFlowPane.getStyles().put("spacing", buttonSpacing);
+    }
+
+    protected void updateButtonData(Component tab) {
+        TabPane tabPane = (TabPane)getComponent();
+        int tabIndex = tabPane.getTabs().indexOf(tab);
+
+        if (tabIndex != -1) {
+            TabButton tabButton =
+                (TabButton)buttonFlowPane.get(tabIndex);
+
+            tabButton.setButtonData(new ButtonData(TabPane.getIcon(tab),
+                TabPane.getName(tab)));
+        }
+    }
+
+    // TabPaneListener methods
+
+    public void tabOrientationChanged(TabPane tabPane) {
+        Orientation tabOrientation = tabPane.getTabOrientation();
+
+        buttonFlowPane.setOrientation(tabOrientation);
+
+        Component.StyleDictionary buttonFlowPaneStyles = buttonFlowPane.getStyles();
+        switch (tabOrientation) {
+            case HORIZONTAL: {
+                buttonFlowPaneStyles.put("horizontalAlignment", HorizontalAlignment.LEFT);
+                buttonFlowPaneStyles.put("verticalAlignment", VerticalAlignment.JUSTIFY);
+                break;
+            }
+
+            case VERTICAL: {
+                buttonFlowPaneStyles.put("horizontalAlignment", HorizontalAlignment.JUSTIFY);
+                buttonFlowPaneStyles.put("verticalAlignment", VerticalAlignment.TOP);
+                break;
+            }
+        }
+    }
+
+    public void collapsibleChanged(TabPane tabPane) {
+        // No-op
+    }
+
+    public void tabInserted(TabPane tabPane, int index) {
+        // Create a new button for the tab
+        Component tab = tabPane.getTabs().get(index);
+        TabButton tabButton = new TabButton(new ButtonData(TabPane.getIcon(tab),
+            TabPane.getName(tab)));
+        tabButton.setGroup(tabButtonGroup);
+
+        buttonFlowPane.insert(tabButton, index);
+    }
+
+    public void tabsRemoved(TabPane tabPane, int index, Sequence<Component> tabs) {
+        // Remove the buttons
+        Sequence<Component> removed = buttonFlowPane.remove(index, tabs.getLength());
+
+        for (int i = 0, n = removed.getLength(); i < n; i++) {
+            TabButton tabButton = (TabButton)removed.get(i);
+            tabButton.setGroup((Group)null);
+        }
+    }
+
+    public void cornerChanged(TabPane tabPane, Component previousCorner) {
+        invalidateComponent();
+    }
+
+    // Tab pane selection events
+    public void selectedIndexChanged(TabPane tabPane, int previousSelectedIndex) {
+        int selectedIndex = tabPane.getSelectedIndex();
+
+        if (selectedIndex == -1) {
+            Button button = tabButtonGroup.getSelection();
+            if (button != null) {
+                button.setSelected(false);
+            }
+        } else {
+            Button button = (Button)buttonFlowPane.get(selectedIndex);
+            button.setSelected(true);
+        }
+
+        invalidateComponent();
+    }
+
+    // Tab pane attribute events
+    public void nameChanged(TabPane tabPane, Component component, String previousName) {
+        updateButtonData(component);
+    }
+
+    public void iconChanged(TabPane tabPane, Component component, Image previousIcon) {
+        updateButtonData(component);
+    }
+
+    // Button group events
+    public void selectionChanged(Group group, Button previousSelection) {
+        Button button = tabButtonGroup.getSelection();
+        int index = (button == null) ? -1 : buttonFlowPane.indexOf(button);
+
+        TabPane tabPane = (TabPane)getComponent();
+        tabPane.setSelectedIndex(index);
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraTablePaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraTablePaneSkin.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraTablePaneSkin.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/skin/terra/TerraTablePaneSkin.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,41 @@
+/*
+ * 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.TablePaneSkin;
+
+/**
+ * Terra table pane skin.
+ *
+ * @author tvolkert
+ */
+public class TerraTablePaneSkin extends TablePaneSkin {
+    public TerraTablePaneSkin() {
+        setGridColor(7);
+        setSelectionBackgroundColor(10);
+    }
+
+    public final void setGridColor(int gridColor) {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setGridColor(theme.getColor(gridColor));
+    }
+
+    public final void setSelectionBackgroundColor(int selectionBackgroundColor) {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        setSelectionBackgroundColor(theme.getColor(selectionBackgroundColor));
+    }
+}