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 [36/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/TerraTextInputSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,1177 @@
+/*
+ * 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.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineMetrics;
+import java.awt.font.TextHitInfo;
+import java.awt.font.TextLayout;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.text.AttributedCharacterIterator;
+
+import pivot.collections.Dictionary;
+import pivot.wtk.ApplicationContext;
+import pivot.wtk.Clipboard;
+import pivot.wtk.Component;
+import pivot.wtk.ComponentMouseButtonListener;
+import pivot.wtk.ComponentMouseListener;
+import pivot.wtk.Cursor;
+import pivot.wtk.Dimensions;
+import pivot.wtk.Display;
+import pivot.wtk.Insets;
+import pivot.wtk.Keyboard;
+import pivot.wtk.Mouse;
+import pivot.wtk.Bounds;
+import pivot.wtk.TextInput;
+import pivot.wtk.TextInputListener;
+import pivot.wtk.TextInputCharacterListener;
+import pivot.wtk.TextInputSelectionListener;
+import pivot.wtk.Theme;
+import pivot.wtk.skin.ComponentSkin;
+
+/**
+ * Text input skin.
+ *
+ * @author gbrown
+ */
+public class TerraTextInputSkin extends ComponentSkin
+    implements TextInputListener, TextInputCharacterListener, TextInputSelectionListener {
+    private class MouseSelectionHandler
+        implements ComponentMouseListener, ComponentMouseButtonListener {
+        private class ScrollSelectionCallback implements Runnable {
+            private int x = 0;
+
+            public void run() {
+                TextInput textInput = (TextInput)getComponent();
+                int selectionStart = textInput.getSelectionStart();
+                int selectionLength = textInput.getSelectionLength();
+
+                if (x < 0) {
+                    // Add the previous character to the selection
+                    if (selectionStart > 0) {
+                        selectionStart--;
+                        selectionLength++;
+                    }
+                } else {
+                    // Add the next character to the selection
+                    if (selectionStart + selectionLength < textInput.getCharacterCount()) {
+                        selectionLength++;
+                    }
+                }
+
+                textInput.setSelection(selectionStart, selectionLength);
+            }
+        }
+
+        private ScrollSelectionCallback scrollSelectionCallback = new ScrollSelectionCallback();
+        private int scrollSelectionIntervalID = -1;
+
+        private static final int SCROLL_RATE = 50;
+
+        public boolean mouseMove(Component component, int x, int y) {
+            String text = getText();
+
+            if (text.length() > 0) {
+                Display display = (Display)component;
+
+                TextInput textInput = (TextInput)getComponent();
+                x = textInput.mapPointFromAncestor(display, x, 0).x;
+
+                if (x >= 0
+                    && x < textInput.getWidth()) {
+                    // Stop the scroll selection timer
+                    if (scrollSelectionIntervalID != -1) {
+                        ApplicationContext.clearInterval(scrollSelectionIntervalID);
+                        scrollSelectionIntervalID = -1;
+                    }
+
+                    // Get the current selection
+                    int selectionStart = textInput.getSelectionStart();
+                    int selectionLength = textInput.getSelectionLength();
+
+                    // Get the insertion index
+                    int index = getInsertionIndex(text, x);
+
+                    if (index < selectionStart) {
+                        selectionLength += (selectionStart - index);
+                        selectionStart = index;
+                    } else {
+                        if (index > selectionStart + selectionLength) {
+                            selectionLength = index - selectionStart;
+                        }
+                    }
+
+                    textInput.setSelection(selectionStart, selectionLength);
+                } else {
+                    scrollSelectionCallback.x = x;
+
+                    if (scrollSelectionIntervalID == -1) {
+                        scrollSelectionIntervalID =
+                            ApplicationContext.setInterval(scrollSelectionCallback, SCROLL_RATE);
+
+                        // Run the callback once now to scroll the selection immediately
+                        scrollSelectionCallback.run();
+                    }
+                }
+            }
+
+            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) {
+            // Stop the scroll selection timer
+            if (scrollSelectionIntervalID != -1) {
+                ApplicationContext.clearInterval(scrollSelectionIntervalID);
+                scrollSelectionIntervalID = -1;
+            }
+
+            // Remove the display mouse listeners
+            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) {
+        }
+    }
+
+    /**
+     * TODO This class will be used to optimize rendering of text, so we don't
+     * need to get a copy of the string via {@link TextInput#getText()}.
+     *
+     * NOTE We'll want to use this everywhere we are currently calling
+     * getText(), if possible. This means that the character iterator will need
+     * to return "*" characters when in password mode.
+     *
+     * @author gbrown
+     */
+    private static class TextInputCharacterIterator implements AttributedCharacterIterator {
+        public TextInputCharacterIterator(TextInput textInput) {
+            // TODO Need index arguments
+        }
+
+        public int getBeginIndex() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public int getEndIndex() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public char setIndex(int position) {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public int getIndex() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public char current() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public char first() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public char last() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public char next() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public char previous() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public int getRunLimit() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public int getRunLimit(Attribute arg0) {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public int getRunLimit(java.util.Set<? extends Attribute> arg0) {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public int getRunStart() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public int getRunStart(Attribute arg0) {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public int getRunStart(java.util.Set<? extends Attribute> arg0) {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        public Object getAttribute(Attribute arg0) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        public java.util.Map<Attribute, Object> getAttributes() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        public java.util.Set<Attribute> getAllAttributeKeys() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        public Object clone() {
+            // TODO
+            return null;
+        }
+    }
+
+    private class BlinkCursorCallback implements Runnable {
+        public void run() {
+            caretOn = !caretOn;
+
+            java.awt.Rectangle caretBounds = caretShapes[0].getBounds();
+            LineMetrics lm = font.getLineMetrics("", fontRenderContext);
+
+            int ascent = Math.round(lm.getAscent());
+            caretBounds.x += (padding.left - scrollLeft + 1);
+            caretBounds.y += (padding.top + ascent + 1);
+
+            if (caretBounds.width == 0) {
+                caretBounds.width++;
+            }
+
+            TextInput textInput = (TextInput)getComponent();
+            textInput.repaint(caretBounds.x, caretBounds.y,
+                caretBounds.width, caretBounds.height, true);
+        }
+    }
+
+    protected FontRenderContext fontRenderContext = new FontRenderContext(null, true, false);
+
+    private boolean caretOn = true;
+    private Shape[] caretShapes = null;
+    private Shape logicalHighlightShape = null;
+
+    private int scrollLeft = 0;
+
+    private BlinkCursorCallback blinkCursorCallback = new BlinkCursorCallback();
+    private int blinkCursorIntervalID = -1;
+
+    private Font font;
+    private Color color;
+    private Color disabledColor;
+    private Color backgroundColor;
+    private Color disabledBackgroundColor;
+    private Color borderColor;
+    private Color disabledBorderColor;
+    private Insets padding;
+
+    private Color selectionColor;
+    private Color selectionBackgroundColor;
+    private Color inactiveSelectionColor;
+    private Color inactiveSelectionBackgroundColor;
+
+    // Derived colors
+    private Color bevelColor;
+    private Color disabledBevelColor;
+
+    public TerraTextInputSkin() {
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        font = theme.getFont();
+        color = theme.getColor(1);
+        disabledColor = theme.getColor(7);
+        backgroundColor = theme.getColor(11);
+        disabledBackgroundColor = theme.getColor(10);
+        borderColor = theme.getColor(7);
+        disabledBorderColor = theme.getColor(7);
+        padding = new Insets(2);
+
+        selectionColor = theme.getColor(4);
+        selectionBackgroundColor = theme.getColor(19);
+        inactiveSelectionColor = theme.getColor(1);
+        inactiveSelectionBackgroundColor = theme.getColor(9);
+
+        // Set the derived colors
+        bevelColor = TerraTheme.darken(backgroundColor);
+        disabledBevelColor = disabledBackgroundColor;
+    }
+
+    @Override
+    public void install(Component component) {
+        super.install(component);
+
+        TextInput textInput = (TextInput)component;
+        textInput.getTextInputCharacterListeners().add(this);
+        textInput.getTextInputSelectionListeners().add(this);
+
+        textInput.setCursor(Cursor.TEXT);
+
+        selectionChanged(textInput, 0, 0);
+    }
+
+    @Override
+    public void uninstall() {
+        TextInput textInput = (TextInput)getComponent();
+        textInput.getTextInputCharacterListeners().remove(this);
+        textInput.getTextInputSelectionListeners().remove(this);
+
+        textInput.setCursor(Cursor.DEFAULT);
+
+        super.uninstall();
+    }
+
+    public int getPreferredWidth(int height) {
+        TextInput textInput = (TextInput)getComponent();
+        int textSize = textInput.getTextSize();
+
+        // TODO Localize?
+        // TODO Recalculate only when font changes
+        String testString = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
+
+        Rectangle2D testStringBounds = font.getStringBounds(testString, fontRenderContext);
+        int averageCharWidth = (int)Math.round((testStringBounds.getWidth() / testString.length()));
+
+        return textSize * averageCharWidth + (padding.left + padding.right) + 2;
+    }
+
+    public int getPreferredHeight(int width) {
+        // TODO Recalculate only when font changes
+        Rectangle2D maxCharBounds = font.getMaxCharBounds(fontRenderContext);
+        int maxCharHeight = (int)Math.round(maxCharBounds.getHeight());
+
+        return maxCharHeight + (padding.top + padding.bottom) + 2;
+    }
+
+    public Dimensions getPreferredSize() {
+        return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+    }
+
+    public void layout() {
+        // No-op
+    }
+
+    public void paint(Graphics2D graphics) {
+        TextInput textInput = (TextInput)getComponent();
+
+        Color backgroundColor = null;
+        Color borderColor = null;
+        Color bevelColor = null;
+
+        if (textInput.isEnabled()) {
+            backgroundColor = this.backgroundColor;
+            borderColor = this.borderColor;
+            bevelColor = this.bevelColor;
+        }
+        else {
+            backgroundColor = disabledBackgroundColor;
+            borderColor = this.disabledBorderColor;
+            bevelColor = disabledBevelColor;
+        }
+
+        // Paint the background
+        graphics.setPaint(backgroundColor);
+        Bounds bounds = new Bounds(0, 0, getWidth(), getHeight());
+        graphics.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
+
+        // Draw all lines with a 1px solid stroke
+        graphics.setStroke(new BasicStroke());
+
+        // Paint the border
+        graphics.setPaint(borderColor);
+        graphics.drawRect(0, 0, bounds.width - 1, bounds.height - 1);
+
+        // Paint the bevel
+        Line2D bevelLine = new Line2D.Double(1, 1, bounds.width - 2, 1);
+        graphics.setPaint(bevelColor);
+        graphics.draw(bevelLine);
+
+        // Paint the content
+        String text = getText();
+
+        LineMetrics lm = font.getLineMetrics("", fontRenderContext);
+        int ascent = Math.round(lm.getAscent());
+
+        graphics.translate(padding.left - scrollLeft + 1, padding.top + ascent + 1);
+
+        if (textInput.getCharacterCount() > 0) {
+            // Paint the text
+            if (fontRenderContext.isAntiAliased()) {
+                // TODO Use VALUE_TEXT_ANTIALIAS_LCD_HRGB when JDK 1.6 is
+                // available on OSX?
+                graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+            }
+
+            if (fontRenderContext.usesFractionalMetrics()) {
+                graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
+                    RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+            }
+
+            graphics.setFont(font);
+            graphics.setPaint(color);
+            graphics.drawString(text, 0, 0);
+
+            if (textInput.getSelectionLength() > 0) {
+                // Paint the selection
+                Graphics2D selectionGraphics = (Graphics2D)graphics.create();
+                selectionGraphics.clip(logicalHighlightShape.getBounds());
+
+                Color selectionColor;
+                Color selectionBackgroundColor;
+
+                if (textInput.isFocused()) {
+                    selectionColor = this.selectionColor;
+                    selectionBackgroundColor = this.selectionBackgroundColor;
+                } else {
+                    selectionColor = inactiveSelectionColor;
+                    selectionBackgroundColor = inactiveSelectionBackgroundColor;
+                }
+
+                selectionGraphics.setPaint(selectionBackgroundColor);
+                selectionGraphics.fill(logicalHighlightShape);
+
+                selectionGraphics.setPaint(selectionColor);
+                selectionGraphics.drawString(text, 0, 0);
+
+                selectionGraphics.dispose();
+            }
+        }
+
+        if (textInput.getSelectionLength() == 0
+            && textInput.isFocused()
+            && caretOn) {
+            graphics.setPaint(Color.BLACK);
+            graphics.draw(caretShapes[0]);
+        }
+    }
+
+    protected String getText() {
+        TextInput textInput = (TextInput)getComponent();
+
+        // TODO Use the internal character iterator instead of getting a copy
+        // of the string
+        String text = textInput.getText();
+
+        if (textInput.isPassword()) {
+            int n = text.length();
+            StringBuilder passwordTextBuilder = new StringBuilder(n);
+            for (int i = 0; i < n; i++) {
+                passwordTextBuilder.append("*");
+            }
+
+            text = passwordTextBuilder.toString();
+        }
+
+        return text;
+    }
+
+    protected int getInsertionIndex(String text, int x) {
+        TextLayout textLayout = new TextLayout(text, font, fontRenderContext);
+        TextHitInfo textHitInfo = textLayout.hitTestChar(x + scrollLeft - padding.left - 1, 0);
+        int index = textHitInfo.getInsertionIndex();
+
+        return index;
+    }
+
+    public void showCaret(boolean show) {
+        if (show) {
+            if (blinkCursorIntervalID == -1) {
+                blinkCursorIntervalID = ApplicationContext.setInterval(blinkCursorCallback,
+                    ApplicationContext.getCursorBlinkRate());
+
+                // Run the callback once now to show the cursor immediately
+                blinkCursorCallback.run();
+            }
+        } else {
+            if (blinkCursorIntervalID != -1) {
+                ApplicationContext.clearInterval(blinkCursorIntervalID);
+                blinkCursorIntervalID = -1;
+            }
+        }
+    }
+
+    public Font getFont() {
+        return font;
+    }
+
+    public void setFont(Font font) {
+        if (font == null) {
+            throw new IllegalArgumentException("font is null.");
+        }
+
+        this.font = font;
+        invalidateComponent();
+    }
+
+    public final void setFont(String font) {
+        if (font == null) {
+            throw new IllegalArgumentException("font is null.");
+        }
+
+        setFont(Font.decode(font));
+    }
+
+    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 getBackgroundColor() {
+        return backgroundColor;
+    }
+
+    public void setBackgroundColor(Color backgroundColor) {
+        if (backgroundColor == null) {
+            throw new IllegalArgumentException("backgroundColor is null.");
+        }
+
+        this.backgroundColor = backgroundColor;
+        bevelColor = TerraTheme.darken(backgroundColor);
+        repaintComponent();
+    }
+
+    public final void setBackgroundColor(String backgroundColor) {
+        if (backgroundColor == null) {
+            throw new IllegalArgumentException("backgroundColor is null.");
+        }
+
+        setBackgroundColor(decodeColor(backgroundColor));
+    }
+
+    public Color getDisabledBackgroundColor() {
+        return disabledBackgroundColor;
+    }
+
+    public void setDisabledBackgroundColor(Color disabledBackgroundColor) {
+        if (disabledBackgroundColor == null) {
+            throw new IllegalArgumentException("disabledBackgroundColor is null.");
+        }
+
+        this.disabledBackgroundColor = disabledBackgroundColor;
+        disabledBevelColor = disabledBackgroundColor;
+        repaintComponent();
+    }
+
+    public final void setDisabledBackgroundColor(String disabledBackgroundColor) {
+        if (disabledBackgroundColor == null) {
+            throw new IllegalArgumentException("disabledBackgroundColor is null.");
+        }
+
+        setDisabledBackgroundColor(decodeColor(disabledBackgroundColor));
+    }
+
+    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 getDisabledBorderColor() {
+        return disabledBorderColor;
+    }
+
+    public void setDisabledBorderColor(Color disabledBorderColor) {
+        if (disabledBorderColor == null) {
+            throw new IllegalArgumentException("disabledBorderColor is null.");
+        }
+
+        this.disabledBorderColor = disabledBorderColor;
+        repaintComponent();
+    }
+
+    public final void setDisabledBorderColor(String disabledBorderColor) {
+        if (disabledBorderColor == null) {
+            throw new IllegalArgumentException("disabledBorderColor is null.");
+        }
+
+        setDisabledBorderColor(decodeColor(disabledBorderColor));
+    }
+
+    public Color getSelectionColor() {
+        return selectionColor;
+    }
+
+    public void setSelectionColor(Color selectionColor) {
+        if (selectionColor == null) {
+            throw new IllegalArgumentException("selectionColor is null.");
+        }
+
+        this.selectionColor = selectionColor;
+        repaintComponent();
+    }
+
+    public final void setSelectionColor(String selectionColor) {
+        if (selectionColor == null) {
+            throw new IllegalArgumentException("selectionColor is null.");
+        }
+
+        setSelectionColor(decodeColor(selectionColor));
+    }
+
+    public Color getSelectionBackgroundColor() {
+        return selectionBackgroundColor;
+    }
+
+    public void setSelectionBackgroundColor(Color selectionBackgroundColor) {
+        if (selectionBackgroundColor == null) {
+            throw new IllegalArgumentException("selectionBackgroundColor is null.");
+        }
+
+        this.selectionBackgroundColor = selectionBackgroundColor;
+        repaintComponent();
+    }
+
+    public final void setSelectionBackgroundColor(String selectionBackgroundColor) {
+        if (selectionBackgroundColor == null) {
+            throw new IllegalArgumentException("selectionBackgroundColor is null.");
+        }
+
+        setSelectionBackgroundColor(decodeColor(selectionBackgroundColor));
+    }
+
+    public Color getInactiveSelectionColor() {
+        return inactiveSelectionColor;
+    }
+
+    public void setInactiveSelectionColor(Color inactiveSelectionColor) {
+        if (inactiveSelectionColor == null) {
+            throw new IllegalArgumentException("inactiveSelectionColor is null.");
+        }
+
+        this.inactiveSelectionColor = inactiveSelectionColor;
+        repaintComponent();
+    }
+
+    public final void setInactiveSelectionColor(String inactiveSelectionColor) {
+        if (inactiveSelectionColor == null) {
+            throw new IllegalArgumentException("inactiveSelectionColor is null.");
+        }
+
+        setInactiveSelectionColor(decodeColor(inactiveSelectionColor));
+    }
+
+    public Color getInactiveSelectionBackgroundColor() {
+        return inactiveSelectionBackgroundColor;
+    }
+
+    public void setInactiveSelectionBackgroundColor(Color inactiveSelectionBackgroundColor) {
+        if (inactiveSelectionBackgroundColor == null) {
+            throw new IllegalArgumentException("inactiveSelectionBackgroundColor is null.");
+        }
+
+        this.inactiveSelectionBackgroundColor = inactiveSelectionBackgroundColor;
+        repaintComponent();
+    }
+
+    public final void setInactiveSelectionBackgroundColor(String inactiveSelectionBackgroundColor) {
+        if (inactiveSelectionBackgroundColor == null) {
+            throw new IllegalArgumentException("inactiveSelectionBackgroundColor is null.");
+        }
+
+        setInactiveSelectionBackgroundColor(decodeColor(inactiveSelectionBackgroundColor));
+    }
+
+    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());
+    }
+
+    @Override
+    public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+        if (button == Mouse.Button.LEFT) {
+            // Move the caret to the insertion point
+            TextInput textInput = (TextInput)getComponent();
+            String text = getText();
+
+            if (text.length() > 0) {
+                int index = getInsertionIndex(text, x);
+                textInput.setSelection(index, 0);
+            }
+
+            // Begin selecting text
+            MouseSelectionHandler mouseSelectionHandler = new MouseSelectionHandler();
+
+            Display display = textInput.getDisplay();
+            display.getComponentMouseListeners().add(mouseSelectionHandler);
+            display.getComponentMouseButtonListeners().add(mouseSelectionHandler);
+
+            // Set focus to the text input
+            textInput.requestFocus();
+        }
+
+        return super.mouseDown(component, button, x, y);
+    }
+
+    @Override
+    public void mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
+        if (button == Mouse.Button.LEFT
+            && count > 1) {
+            TextInput textInput = (TextInput)getComponent();
+            textInput.setSelection(0, textInput.getCharacterCount());
+        }
+
+        super.mouseClick(component, button, x, y, count);
+    }
+
+    @Override
+    public void keyTyped(Component component, char character) {
+        // Ignore characters in the control range and the ASCII delete
+        // character
+        if (character > 0x1F
+            && character != 0x7F) {
+            TextInput textInput = (TextInput)getComponent();
+
+            int selectionStart = textInput.getSelectionStart();
+            int selectionLength = textInput.getSelectionLength();
+
+            if (selectionLength > 0) {
+                textInput.removeText(selectionStart, selectionLength);
+            }
+
+            if (textInput.getCharacterCount() < textInput.getMaximumLength()) {
+                textInput.insertText(character, selectionStart);
+            } else {
+                ApplicationContext.beep();
+            }
+        }
+    }
+
+    @Override
+    public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+        boolean consumed = false;
+
+        TextInput textInput = (TextInput)getComponent();
+
+        if (keyCode == Keyboard.KeyCode.DELETE) {
+            int selectionStart = textInput.getSelectionStart();
+            int selectionLength = textInput.getSelectionLength();
+
+            if (selectionLength > 0) {
+                // Delete any selected text
+                textInput.removeText(selectionStart, selectionLength);
+            } else {
+                // Delete the character after the caret (if any)
+                if (selectionStart < textInput.getCharacterCount()) {
+                    textInput.removeText(selectionStart, 1);
+                }
+            }
+        } else if (keyCode == Keyboard.KeyCode.BACKSPACE) {
+            int selectionLength = textInput.getSelectionLength();
+
+            if (selectionLength > 0) {
+                // Delete any selected text
+                textInput.removeText(textInput.getSelectionStart(), selectionLength);
+            } else {
+                // Delete the character before the caret (if any)
+                int selectionStart = textInput.getSelectionStart();
+
+                if (selectionStart > 0) {
+                    textInput.removeText(selectionStart - 1, 1);
+                }
+            }
+        } else if (keyCode == Keyboard.KeyCode.LEFT) {
+            int selectionStart = textInput.getSelectionStart();
+            int selectionLength = textInput.getSelectionLength();
+
+            if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)
+                && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+                // Add all preceding text to the selection
+                selectionLength = selectionStart + selectionLength;
+                selectionStart = 0;
+            } else if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+                // Add the previous character to the selection
+                if (selectionStart > 0) {
+                    selectionStart--;
+                    selectionLength++;
+                }
+            } else if (Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+                // Clear the selection and move the caret to the beginning of
+                // the text
+                selectionStart = 0;
+                selectionLength = 0;
+            } else {
+                // Clear the selection and move the caret back by one
+                // character
+                if (selectionLength == 0
+                    && selectionStart > 0) {
+                    selectionStart--;
+                }
+
+                selectionLength = 0;
+            }
+
+            textInput.setSelection(selectionStart, selectionLength);
+        } else if (keyCode == Keyboard.KeyCode.RIGHT) {
+            int selectionStart = textInput.getSelectionStart();
+            int selectionLength = textInput.getSelectionLength();
+
+            if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)
+                && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+                // Add all subsequent text to the selection
+                selectionLength = textInput.getCharacterCount() - selectionStart;
+            } else if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
+                // Add the next character to the selection
+                if (selectionStart + selectionLength < textInput.getCharacterCount()) {
+                    selectionLength++;
+                }
+            } else if (Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+                // Clear the selection and move the caret to the end of
+                // the text
+                selectionStart = textInput.getCharacterCount();
+                selectionLength = 0;
+            } else {
+                // Clear the selection and move the caret forward by one
+                // character
+                selectionStart += selectionLength;
+
+                if (selectionLength == 0
+                    && selectionStart < textInput.getCharacterCount()) {
+                    selectionStart++;
+                }
+
+                selectionLength = 0;
+            }
+
+            textInput.setSelection(selectionStart, selectionLength);
+        } else if (keyCode == Keyboard.KeyCode.A
+            && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+            // Select all
+            textInput.setSelection(0, textInput.getCharacterCount());
+        } else if (keyCode == Keyboard.KeyCode.X
+            && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+            // "Cut"
+            if (textInput.isPassword()) {
+                ApplicationContext.beep();
+            } else {
+                // Delete any selected text and put it on the clipboard
+                int selectionLength = textInput.getSelectionLength();
+                if (selectionLength > 0) {
+                    String removedText = textInput.removeText(textInput.getSelectionStart(),
+                        selectionLength);
+
+                    Clipboard.getInstance().put(removedText);
+                }
+            }
+        } else if (keyCode == Keyboard.KeyCode.C
+            && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+            // "Copy"
+            if (textInput.isPassword()) {
+                ApplicationContext.beep();
+            } else {
+                // Copy selection to clipboard
+                String selectedText = textInput.getSelectedText();
+                if (selectedText != null) {
+                    Clipboard.getInstance().put(selectedText);
+                }
+            }
+        } else if (keyCode == Keyboard.KeyCode.V
+            && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
+            // "Paste"
+            Object clipboardContents = Clipboard.getInstance().get();
+
+            if (clipboardContents != null) {
+                // Paste the string representation of the content
+                String clipboardText = clipboardContents.toString();
+
+                if ((clipboardText.length()
+                    + textInput.getCharacterCount()) > textInput.getMaximumLength()) {
+                    ApplicationContext.beep();
+                } else {
+                    // Remove any existing selection
+                    int selectionLength = textInput.getSelectionLength();
+                    if (selectionLength > 0) {
+                        textInput.removeText(textInput.getSelectionStart(),
+                            selectionLength);
+                    }
+
+                    // Insert the clipboard contents
+                    int selectionStart = textInput.getSelectionStart();
+                    textInput.insertText(clipboardText, selectionStart);
+                }
+            }
+        } else {
+            consumed = super.keyPressed(component, keyCode, keyLocation);
+        }
+
+        return consumed;
+    }
+
+    // Component state events
+    @Override
+    public void enabledChanged(Component component) {
+        super.enabledChanged(component);
+
+        repaintComponent();
+    }
+
+    @Override
+    public void focusedChanged(Component component, boolean temporary) {
+        super.focusedChanged(component, temporary);
+
+        TextInput textInput = (TextInput)getComponent();
+
+        if (component.isFocused()) {
+            showCaret(textInput.getSelectionLength() == 0);
+
+            if (!temporary
+                && Mouse.getButtons() == 0) {
+                textInput.setSelection(0, textInput.getCharacterCount());
+            }
+        } else {
+            if (!temporary) {
+                textInput.setSelection(textInput.getSelectionStart()
+                    + textInput.getSelectionLength(), 0);
+            }
+
+            showCaret(false);
+        }
+
+        repaintComponent();
+    }
+
+    // Text input events
+    public void textKeyChanged(TextInput textInput, String previousTextKey) {
+        // No-op
+    }
+
+    public void textSizeChanged(TextInput textInput, int previousTextSize) {
+        invalidateComponent();
+    }
+
+    public void maximumLengthChanged(TextInput textInput, int previousMaximumLength) {
+        // No-op
+    }
+
+    public void passwordChanged(TextInput textInput) {
+        repaintComponent();
+    }
+
+    // Text input character events
+    public void charactersInserted(TextInput textInput, int index, int count) {
+        repaintComponent();
+    }
+
+    public void charactersRemoved(TextInput textInput, int index, int count) {
+        String text = getText();
+        Rectangle2D textBounds = font.getStringBounds(text, fontRenderContext);
+
+        int textWidth = (int)textBounds.getWidth();
+        int width = getWidth();
+
+        // If the right edge of the text is less than the right inset, align
+        // the text's right edge with the inset
+        if (textWidth - scrollLeft + padding.left + 1 < width - padding.right - 1) {
+            scrollLeft = Math.max(textWidth + (padding.left + padding.right + 2) - width, 0);
+        }
+
+        repaintComponent();
+    }
+
+    public void charactersReset(TextInput textInput) {
+        repaintComponent();
+    }
+
+    // Text input selection events
+    public void selectionChanged(TextInput textInput,
+        int previousSelectionStart, int previousSelectionLength) {
+        // Update the selection bounding box
+        String text = getText();
+
+        // NOTE For some reason, TextLayout does not accept zero-length
+        // strings. This may be a bug in AWT, since an empty string should be
+        // valid, and is necessary to determine the caret shape for an empty
+        // text input.
+        // TODO Report this issue to Sun?
+        if (text.length() == 0) {
+            text = " ";
+        }
+
+        int selectionStart = textInput.getSelectionStart();
+        int selectionLength = textInput.getSelectionLength();
+
+        TextLayout textLayout = new TextLayout(text, font, fontRenderContext);
+
+        caretShapes = textLayout.getCaretShapes(selectionStart);
+        logicalHighlightShape = textLayout.getLogicalHighlightShape(selectionStart,
+            selectionStart + selectionLength);
+
+        int width = getWidth();
+
+        if (width <= padding.left + padding.right + 2) {
+            scrollLeft = 0;
+        } else {
+            if (textInput.getSelectionLength() == 0) {
+                Rectangle2D caretBounds = caretShapes[0].getBounds();
+                int caretLeft = (int)caretBounds.getX();
+
+                if (caretLeft - scrollLeft < 0) {
+                    // Ensure that the left edge of caret is visible
+                    scrollLeft = caretLeft;
+                } else {
+                    // Ensure that the right edge of the caret is visible
+                    int caretRight = (int)caretBounds.getMaxX();
+
+                    if (caretRight - scrollLeft + padding.left + 1 > width - padding.right - 1) {
+                        scrollLeft = Math.max(caretRight
+                            - (width - (padding.left + padding.right + 2)), 0);
+                    }
+                }
+            } else {
+                Rectangle2D logicalHighlightBounds = logicalHighlightShape.getBounds();
+                int logicalHighlightLeft = (int)logicalHighlightBounds.getX();
+
+                if (logicalHighlightLeft - scrollLeft < 0
+                    && previousSelectionStart > selectionStart) {
+                    // Ensure that the left edge of the highlight is visible
+                    scrollLeft = logicalHighlightLeft;
+                } else {
+                    // Ensure that the right edge of the highlight is visible
+                    int logicalHighlightRight = (int)logicalHighlightBounds.getMaxX();
+
+                    if (logicalHighlightRight - scrollLeft + padding.left + 1 > width - padding.right - 1) {
+                        scrollLeft = Math.max(logicalHighlightRight
+                            - (width - (padding.left + padding.right + 2)), 0);
+                    }
+                }
+            }
+        }
+
+        showCaret(textInput.isFocused()
+            && selectionLength == 0);
+
+        repaintComponent();
+    }
+}

Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,297 @@
+/*
+ * 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.Font;
+import java.net.URL;
+
+import pivot.collections.List;
+import pivot.serialization.JSONSerializer;
+import pivot.util.Resources;
+import pivot.wtk.Alert;
+import pivot.wtk.ApplicationContext;
+import pivot.wtk.Border;
+import pivot.wtk.Calendar;
+import pivot.wtk.Checkbox;
+import pivot.wtk.Dialog;
+import pivot.wtk.Expander;
+import pivot.wtk.FlowPane;
+import pivot.wtk.Form;
+import pivot.wtk.Frame;
+import pivot.wtk.Label;
+import pivot.wtk.LinkButton;
+import pivot.wtk.ListButton;
+import pivot.wtk.ListView;
+import pivot.wtk.MenuBar;
+import pivot.wtk.MenuButton;
+import pivot.wtk.MenuPopup;
+import pivot.wtk.Menu;
+import pivot.wtk.MessageType;
+import pivot.wtk.Meter;
+import pivot.wtk.Palette;
+import pivot.wtk.Panorama;
+import pivot.wtk.Prompt;
+import pivot.wtk.PushButton;
+import pivot.wtk.RadioButton;
+import pivot.wtk.Rollup;
+import pivot.wtk.ScrollBar;
+import pivot.wtk.ScrollPane;
+import pivot.wtk.Separator;
+import pivot.wtk.Sheet;
+import pivot.wtk.Spinner;
+import pivot.wtk.SplitPane;
+import pivot.wtk.TablePane;
+import pivot.wtk.TableViewHeader;
+import pivot.wtk.TableView;
+import pivot.wtk.TabPane;
+import pivot.wtk.TextInput;
+import pivot.wtk.Theme;
+import pivot.wtk.Tooltip;
+import pivot.wtk.TreeView;
+import pivot.wtk.media.Image;
+
+/**
+ * Terra theme.
+ *
+ * @author gbrown
+ * @author tvolkert
+ */
+public final class TerraTheme extends Theme {
+    private String scheme;
+
+    private Resources resources = null;
+
+    private Font font;
+    private Color[] colors;
+
+    public TerraTheme() {
+        this("default");
+    }
+
+    public TerraTheme(String scheme) {
+        if (scheme == null) {
+            throw new IllegalArgumentException("scheme is null.");
+        }
+
+        this.scheme = scheme;
+
+        componentSkinMap.put(Alert.class, TerraAlertSkin.class);
+        componentSkinMap.put(Border.class, TerraBorderSkin.class);
+        componentSkinMap.put(Checkbox.class, TerraCheckboxSkin.class);
+        componentSkinMap.put(Calendar.class, TerraCalendarSkin.class);
+        componentSkinMap.put(Dialog.class, TerraDialogSkin.class);
+        componentSkinMap.put(Expander.class, TerraExpanderSkin.class);
+        componentSkinMap.put(Form.class, TerraFormSkin.class);
+        componentSkinMap.put(FlowPane.class, TerraFlowPaneSkin.class);
+        componentSkinMap.put(Frame.class, TerraFrameSkin.class);
+        componentSkinMap.put(Label.class, TerraLabelSkin.class);
+        componentSkinMap.put(LinkButton.class, TerraLinkButtonSkin.class);
+        componentSkinMap.put(ListButton.class, TerraListButtonSkin.class);
+        componentSkinMap.put(ListView.class, TerraListViewSkin.class);
+        componentSkinMap.put(Menu.class, TerraMenuSkin.class);
+        componentSkinMap.put(Menu.Item.class, TerraMenuItemSkin.class);
+        componentSkinMap.put(MenuBar.class, TerraMenuBarSkin.class);
+        componentSkinMap.put(MenuBar.Item.class, TerraMenuBarItemSkin.class);
+        componentSkinMap.put(MenuButton.class, TerraMenuButtonSkin.class);
+        componentSkinMap.put(MenuPopup.class, TerraMenuPopupSkin.class);
+        componentSkinMap.put(Meter.class, TerraMeterSkin.class);
+        componentSkinMap.put(Palette.class, TerraPaletteSkin.class);
+        componentSkinMap.put(Panorama.class, TerraPanoramaSkin.class);
+        componentSkinMap.put(Prompt.class, TerraPromptSkin.class);
+        componentSkinMap.put(PushButton.class, TerraPushButtonSkin.class);
+        componentSkinMap.put(RadioButton.class, TerraRadioButtonSkin.class);
+        componentSkinMap.put(Rollup.class, TerraRollupSkin.class);
+        componentSkinMap.put(ScrollBar.class, TerraScrollBarSkin.class);
+        componentSkinMap.put(ScrollPane.class, TerraScrollPaneSkin.class);
+        componentSkinMap.put(ScrollPane.Corner.class, TerraScrollPaneCornerSkin.class);
+        componentSkinMap.put(Separator.class, TerraSeparatorSkin.class);
+        componentSkinMap.put(Sheet.class, TerraSheetSkin.class);
+        componentSkinMap.put(Spinner.class, TerraSpinnerSkin.class);
+        componentSkinMap.put(SplitPane.class, TerraSplitPaneSkin.class);
+        componentSkinMap.put(TablePane.class, TerraTablePaneSkin.class);
+        componentSkinMap.put(TableViewHeader.class, TerraTableViewHeaderSkin.class);
+        componentSkinMap.put(TableView.class, TerraTableViewSkin.class);
+        componentSkinMap.put(TabPane.class, TerraTabPaneSkin.class);
+        componentSkinMap.put(TextInput.class, TerraTextInputSkin.class);
+        componentSkinMap.put(Tooltip.class, TerraTooltipSkin.class);
+        componentSkinMap.put(TreeView.class, TerraTreeViewSkin.class);
+
+        componentSkinMap.put(TerraExpanderSkin.ShadeButton.class, TerraExpanderSkin.ShadeButtonSkin.class);
+        componentSkinMap.put(TerraFrameSkin.FrameButton.class, TerraFrameSkin.FrameButtonSkin.class);
+        componentSkinMap.put(TerraRollupSkin.RollupButton.class, TerraRollupSkin.RollupButtonSkin.class);
+        componentSkinMap.put(TerraScrollBarSkin.ScrollButton.class, TerraScrollBarSkin.ScrollButtonSkin.class);
+        componentSkinMap.put(TerraScrollBarSkin.ScrollHandle.class, TerraScrollBarSkin.ScrollHandleSkin.class);
+        componentSkinMap.put(TerraSpinnerSkin.SpinButton.class, TerraSpinnerSkin.SpinButtonSkin.class);
+        componentSkinMap.put(TerraSpinnerSkin.SpinnerContent.class, TerraSpinnerSkin.SpinnerContentSkin.class);
+        componentSkinMap.put(TerraSplitPaneSkin.Splitter.class, TerraSplitPaneSkin.SplitterSkin.class);
+        componentSkinMap.put(TerraSplitPaneSkin.SplitterShadow.class, TerraSplitPaneSkin.SplitterShadowSkin.class);
+        componentSkinMap.put(TerraTabPaneSkin.TabButton.class, TerraTabPaneSkin.TabButtonSkin.class);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void install() {
+        try {
+            String baseName = getClass().getName() + "_" + scheme;
+            resources = new Resources(baseName);
+        } catch(Exception exception) {
+            throw new RuntimeException(exception);
+        }
+
+        font = Font.decode(JSONSerializer.getString(resources, "font"));
+
+        List<String> colorCodes = (List<String>)JSONSerializer.getList(resources, "colors");
+        colors = new Color[colorCodes.getLength() * 3];
+
+        for (int i = 0, n = colorCodes.getLength(); i < n; i++) {
+            int baseIndex = i * 3 + 1;
+            Color baseColor = Color.decode(colorCodes.get(i));
+
+            colors[baseIndex] = baseColor;
+            colors[baseIndex - 1] = darken(baseColor);
+            colors[baseIndex + 1] = brighten(baseColor);
+        }
+    }
+
+    public void uninstall() {
+        resources = null;
+    }
+
+    public Font getFont() {
+        return font;
+    }
+
+    public Color getColor(int index) {
+        return colors[index];
+    }
+
+    public Image getMessageIcon(MessageType messageType) {
+        String messageIconName;
+
+        switch (messageType) {
+            case ERROR: {
+                messageIconName = JSONSerializer.getString(resources, "errorIcon");
+                break;
+            }
+
+            case WARNING: {
+                messageIconName = JSONSerializer.getString(resources, "warningIcon");
+                break;
+            }
+
+            case QUESTION: {
+                messageIconName = JSONSerializer.getString(resources, "questionIcon");
+                break;
+            }
+
+            case INFO: {
+                messageIconName = JSONSerializer.getString(resources, "infoIcon");
+                break;
+            }
+
+            case APPLICATION: {
+                messageIconName = null;
+                break;
+            }
+
+            default: {
+                throw new IllegalArgumentException();
+            }
+        }
+
+        Image messageIcon = null;
+
+        if (messageIconName != null) {
+            URL location = getClass().getResource(messageIconName);
+            messageIcon = (Image)ApplicationContext.getResourceCache().get(location);
+
+            if (messageIcon == null) {
+                messageIcon = Image.load(location);
+                ApplicationContext.getResourceCache().put(location, messageIcon);
+            }
+        }
+
+        return messageIcon;
+    }
+
+    public Image getSmallMessageIcon(MessageType messageType) {
+        String smallMessageIconName;
+
+        switch (messageType) {
+            case ERROR: {
+                smallMessageIconName = JSONSerializer.getString(resources, "smallErrorIcon");
+                break;
+            }
+
+            case WARNING: {
+                smallMessageIconName = JSONSerializer.getString(resources, "smallWarningIcon");
+                break;
+            }
+
+            case QUESTION: {
+                smallMessageIconName = JSONSerializer.getString(resources, "smallQuestionIcon");
+                break;
+            }
+
+            case INFO: {
+                smallMessageIconName = JSONSerializer.getString(resources, "smallInfoIcon");
+                break;
+            }
+
+            case APPLICATION: {
+                smallMessageIconName = null;
+                break;
+            }
+
+            default: {
+                throw new IllegalArgumentException();
+            }
+        }
+
+        Image smallMessageIcon = null;
+
+        if (smallMessageIconName != null) {
+            URL location = getClass().getResource(smallMessageIconName);
+            smallMessageIcon = (Image)ApplicationContext.getResourceCache().get(location);
+
+            if (smallMessageIcon == null) {
+                smallMessageIcon = Image.load(location);
+                ApplicationContext.getResourceCache().put(location, smallMessageIcon);
+            }
+        }
+
+        return smallMessageIcon;
+    }
+
+    public Resources getResources() {
+        return resources;
+    }
+
+    public static Color brighten(Color color) {
+        return adjustBrightness(color, 0.1f);
+    }
+
+    public static Color darken(Color color) {
+        return adjustBrightness(color, -0.1f);
+    }
+
+    private static Color adjustBrightness(Color color, float adjustment) {
+        float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
+        hsb[2] = Math.min(Math.max(hsb[2] + adjustment, 0f), 1f);
+        int rgb = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]);
+        return new Color((color.getAlpha() << 24) | (rgb & 0xffffff));
+    }
+}

Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme_default.json
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme_default.json?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme_default.json (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme_default.json Mon Mar 16 16:16:40 2009
@@ -0,0 +1,23 @@
+{   font: "Verdana 11",
+
+    colors: [
+        "#000000",
+        "#ffffff",
+        "#999999",
+        "#dddcd5",
+        "#2b5580",
+        "#3c77b2",
+        "#14538b",
+        "#b0000f"
+    ],
+
+    errorIcon: "message_type-error-32x32.png",
+    warningIcon: "message_type-warning-32x32.png",
+    questionIcon: "message_type-question-32x32.png",
+    infoIcon: "message_type-info-32x32.png",
+
+    smallErrorIcon: "message_type-error-16x16.png",
+    smallWarningIcon: "message_type-warning-16x16.png",
+    smallQuestionIcon: "message_type-question-16x16.png",
+    smallInfoIcon: "message_type-info-16x16.png"
+}

Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme_test.json
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme_test.json?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme_test.json (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTheme_test.json Mon Mar 16 16:16:40 2009
@@ -0,0 +1,23 @@
+{   font: "Verdana 11",
+
+    colors: [
+        "#404039",
+        "#ffffff",
+        "#998e8a",
+        "#e6e2dc",
+        "#cc6600",
+        "#ff7300",
+        "#851506",
+        "#a32900"
+    ],
+
+    errorIcon: "message_type-error-32x32.png",
+    warningIcon: "message_type-warning-32x32.png",
+    questionIcon: "message_type-question-32x32.png",
+    infoIcon: "message_type-info-32x32.png",
+
+    smallErrorIcon: "message_type-error-16x16.png",
+    smallWarningIcon: "message_type-warning-16x16.png",
+    smallQuestionIcon: "message_type-question-16x16.png",
+    smallInfoIcon: "message_type-info-16x16.png"
+}

Added: incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTooltipSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTooltipSkin.java?rev=754926&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTooltipSkin.java (added)
+++ incubator/pivot/tags/v1.0/wtk/src/pivot/wtk/skin/terra/TerraTooltipSkin.java Mon Mar 16 16:16:40 2009
@@ -0,0 +1,202 @@
+/*
+ * 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 pivot.util.Vote;
+import pivot.wtk.Border;
+import pivot.wtk.Component;
+import pivot.wtk.ComponentMouseListener;
+import pivot.wtk.ComponentMouseButtonListener;
+import pivot.wtk.ComponentMouseWheelListener;
+import pivot.wtk.ComponentKeyListener;
+import pivot.wtk.Display;
+import pivot.wtk.Insets;
+import pivot.wtk.Keyboard;
+import pivot.wtk.Label;
+import pivot.wtk.Mouse;
+import pivot.wtk.Theme;
+import pivot.wtk.Tooltip;
+import pivot.wtk.TooltipListener;
+import pivot.wtk.Window;
+import pivot.wtk.effects.Transition;
+import pivot.wtk.effects.TransitionListener;
+import pivot.wtk.skin.WindowSkin;
+
+/**
+ * Tooltip skin.
+ *
+ * @author gbrown
+ */
+public class TerraTooltipSkin extends WindowSkin implements TooltipListener {
+    private class CloseHandler implements ComponentMouseListener,
+        ComponentMouseButtonListener, ComponentMouseWheelListener,
+        ComponentKeyListener {
+        // Component mouse events
+        public boolean mouseMove(Component component, int x, int y) {
+            Tooltip tooltip = (Tooltip)getComponent();
+            tooltip.close();
+            return false;
+        }
+
+        public void mouseOver(Component component) {
+        }
+
+        public void mouseOut(Component component) {
+        }
+
+        public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
+            Tooltip tooltip = (Tooltip)getComponent();
+            tooltip.close();
+            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) {
+        }
+
+        public boolean mouseWheel(Component component, Mouse.ScrollType scrollType,
+            int scrollAmount, int wheelRotation, int x, int y) {
+            Tooltip tooltip = (Tooltip)getComponent();
+            tooltip.close();
+            return false;
+        }
+
+        // Component key events
+        public void keyTyped(Component component, char character) {
+        }
+
+        public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+            Tooltip tooltip = (Tooltip)getComponent();
+            tooltip.close();
+            return false;
+        }
+
+        public boolean keyReleased(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+            return false;
+        }
+    }
+
+    private Label label = new Label();
+    private Border border = new Border();
+
+    private CloseHandler closeHandler = new CloseHandler();
+    private Transition closeTransition = null;
+
+    private static final int CLOSE_TRANSITION_DURATION = 750;
+    private static final int CLOSE_TRANSITION_RATE = 30;
+
+    public TerraTooltipSkin() {
+        setBackgroundColor((Color)null);
+
+        // Add the label to the border
+        border.setContent(label);
+
+        // Apply the default styles
+        TerraTheme theme = (TerraTheme)Theme.getTheme();
+        Component.StyleDictionary labelStyles = label.getStyles();
+        labelStyles.put("font", theme.getFont());
+        labelStyles.put("color", Color.BLACK);
+
+        Component.StyleDictionary borderStyles = border.getStyles();
+        borderStyles.put("backgroundColor", new Color(0xff, 0xff, 0xe0, 0xf0));
+        borderStyles.put("color", Color.BLACK);
+        borderStyles.put("padding", new Insets(2));
+    }
+
+    @Override
+    public void install(Component component) {
+        super.install(component);
+
+        Tooltip tooltip = (Tooltip)component;
+        tooltip.setContent(border);
+        tooltip.getTooltipListeners().add(this);
+
+        label.setText(tooltip.getText());
+    }
+
+    @Override
+    public void uninstall() {
+        Tooltip tooltip = (Tooltip)getComponent();
+        tooltip.setContent(null);
+        tooltip.getTooltipListeners().remove(this);
+    }
+
+    @Override
+    public void windowOpened(Window window) {
+        super.windowOpened(window);
+
+        // Add this as a display mouse and key listener
+        Display display = window.getDisplay();
+        display.getComponentMouseListeners().add(closeHandler);
+        display.getComponentMouseButtonListeners().add(closeHandler);
+        display.getComponentMouseWheelListeners().add(closeHandler);
+        display.getComponentKeyListeners().add(closeHandler);
+    }
+
+    @Override
+    public Vote previewWindowClose(final Window window) {
+        Vote vote = Vote.APPROVE;
+
+        if (closeTransition == null) {
+            closeTransition = new FadeTransition(window,
+                CLOSE_TRANSITION_DURATION, CLOSE_TRANSITION_RATE);
+
+            closeTransition.start(new TransitionListener() {
+                public void transitionCompleted(Transition transition) {
+                    window.close();
+                }
+            });
+
+            vote = Vote.DEFER;
+        } else {
+            vote = (closeTransition.isRunning()) ? Vote.DEFER : Vote.APPROVE;
+        }
+
+        return vote;
+    }
+
+    @Override
+    public void windowCloseVetoed(Window window, Vote reason) {
+        super.windowCloseVetoed(window, reason);
+
+        if (reason == Vote.DENY
+            && closeTransition != null) {
+            closeTransition.stop();
+        }
+    }
+
+    @Override
+    public void windowClosed(Window window, Display display) {
+        super.windowClosed(window, display);
+
+        // Remove this as a display mouse and key listener
+        display.getComponentMouseListeners().remove(closeHandler);
+        display.getComponentMouseButtonListeners().remove(closeHandler);
+        display.getComponentMouseWheelListeners().remove(closeHandler);
+        display.getComponentKeyListeners().remove(closeHandler);
+
+        closeTransition = null;
+    }
+
+    public void textChanged(Tooltip tooltip, String previousText) {
+        label.setText(tooltip.getText());
+    }
+}