You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by sm...@apache.org on 2011/12/24 17:07:40 UTC

svn commit: r1222994 [10/11] - in /pivot/trunk: core/test/org/apache/pivot/beans/test/ demos/src/org/apache/pivot/demos/memorygame/ demos/src/org/apache/pivot/demos/memorygame/img/ demos/src/org/apache/pivot/demos/roweditor/ demos/www/ etc/ examples/sr...

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/FillPaneListener.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/FillPaneListener.java?rev=1222994&r1=1222993&r2=1222994&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/FillPaneListener.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/FillPaneListener.java Sat Dec 24 16:07:37 2011
@@ -1,29 +1,29 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you 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 org.apache.pivot.wtk;
-
-/**
- * Fill pane listener interface.
- */
-public interface FillPaneListener {
-    /**
-     * Called when a fill pane's orientation has changed.
-     *
-     * @param fillPane
-     */
-    public void orientationChanged(FillPane fillPane);
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk;
+
+/**
+ * Fill pane listener interface.
+ */
+public interface FillPaneListener {
+    /**
+     * Called when a fill pane's orientation has changed.
+     *
+     * @param fillPane
+     */
+    public void orientationChanged(FillPane fillPane);
+}

Propchange: pivot/trunk/wtk/src/org/apache/pivot/wtk/FillPaneListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/RadioButtonGroup.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/RadioButtonGroup.java?rev=1222994&r1=1222993&r2=1222994&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/RadioButtonGroup.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/RadioButtonGroup.java Sat Dec 24 16:07:37 2011
@@ -1,614 +1,614 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you 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 org.apache.pivot.wtk;
-
-import java.util.Iterator;
-
-import org.apache.pivot.collections.ArrayList;
-import org.apache.pivot.collections.Group;
-import org.apache.pivot.collections.List;
-import org.apache.pivot.collections.Sequence;
-import org.apache.pivot.util.Filter;
-import org.apache.pivot.util.ImmutableIterator;
-import org.apache.pivot.wtk.Keyboard.KeyCode;
-import org.apache.pivot.wtk.Keyboard.KeyLocation;
-import org.apache.pivot.wtk.Keyboard.Modifier;
-
-/**
- * Extension of {@link ButtonGroup} providing keyboard navigation within the
- * group and modified focus navigation that treats the group as a single
- * focusable entity.<br/><br/>
- *
- * {@link KeyCode#UP UP} & {@link KeyCode#LEFT LEFT} Select the previous
- * button<br/>
- * {@link KeyCode#DOWN DOWN} & {@link KeyCode#RIGHT RIGHT} Select the next
- * button<br/>
- * {@link KeyCode#HOME HOME} Select the first button<br/>
- * {@link KeyCode#END END} Select the last button<br/><br/>
- *
- * (Note that only {@link Component#isFocusable() focusable} buttons are
- * considered when searching for a Button to select)<br/><br/>
- *
- * When a button within the group is focused and key is typed, an attempt is
- * made to find the next button (default) or previous button (when the SHIFT
- * modifier is pressed) whose renderer text starts with the typed character.
- * This search will always behave as if the <code>circular</code> property were
- * set.<br/><br/>
- *
- * By default, {@link KeyCode#TAB TAB} and {@link KeyCode#TAB SHIFT+TAB}
- * key presses will transfer focus out of the group (forwards or backwards
- * respectively).
- * This is managed by the {@link #setIntraGroupFocusTransferEnabled(boolean)
- * intraGroupFocusTransferEnabled} property.<br/><br/>
- *
- * The {@link #setCircular(boolean) circular} property can be enabled to allow
- * the selection to transfer seamlessly from one end of the group to the other.
- * (i.e. holding down an arrow key will cycle through all focusable buttons)
- * <br/><br/>
- *
- * Note that due to the conflicting return types of the <code>add(T)</code> and
- * <code>remove(T)</code> methods in the {@link Group#add(Object) Group} and
- * {@link Sequence#add(Object) Sequence} interfaces, this class cannot actually
- * implement <code>Sequence&lt;Button&gt;</code>, although most of the same
- * methods are implemented.<br/>
- */
-public class RadioButtonGroup extends ButtonGroup {
-
-    /**
-     * Filter used to determine selectable buttons whose rendered data starts
-     * with the target character.
-     */
-    private class FirstCharacterFilter implements Filter<Integer> {
-        private char target = '\0';
-
-        public void setTarget(char target) {
-            this.target = Character.toUpperCase(target);
-        }
-
-        @Override
-        public boolean include(Integer index) {
-            boolean include = defaultFilter.include(index);
-            if (include) {
-                Button button = buttonOrder.get(index);
-                String rendered = button.getDataRenderer().toString(button.getButtonData());
-                if (rendered != null && rendered.length() > 0) {
-                    char first = Character.toUpperCase(rendered.charAt(0));
-                    if (first != target) {
-                        include = false;
-                    }
-                }
-            }
-            return include;
-        }
-    }
-
-    /**
-     * ComponentKeyListener to be applied to all buttons as they are added to
-     * the group.<br/><br/>
-     *
-     * At least one button in the group must be focused for this listener to be
-     * executed, but that won't necessarily be a selected button.<br/>
-     * This also means that the group will not be empty, although some of the
-     * buttons contained within may not be focusable, or even visible.
-     */
-    private final ComponentKeyListener componentKeyListener = new ComponentKeyListener.Adapter() {
-        /**
-         * Handle TAB & SHIFT+TAB focus traversal, HOME, END & arrow keys
-         */
-        @Override
-        public boolean keyPressed(Component component, int keyCode, KeyLocation keyLocation) {
-            int modifiers = Keyboard.getModifiers();
-            boolean handled = false;
-
-            /*
-             * Potentially transfer focus away from the buttons in this group.
-             *
-             * At this point we know that at least one button is focused, so we
-             * just need to find the first or last (and possibly only) focusable
-             * button depending on the focus transfer direction and then
-             * transfer away from it.
-             */
-            if (!intraGroupFocusTransferEnabled) {
-                if (keyCode == KeyCode.TAB) {
-                    if (modifiers == 0) {
-                        Button lastFocusableButton = get(findPrevious(buttonOrder.getLength()));
-                        lastFocusableButton.transferFocus(FocusTraversalDirection.FORWARD);
-                        handled = true;
-                    } else if (modifiers == Modifier.SHIFT.getMask()) {
-                        Button firstFocusableButton = get(findNext(NO_SELECTION_INDEX));
-                        firstFocusableButton.transferFocus(FocusTraversalDirection.BACKWARD);
-                        handled = true;
-                    }
-                }
-            }
-
-            // Navigation/selection within the group
-            if (!handled && modifiers == 0) {
-                RadioButtonGroup radioButtonGroup = RadioButtonGroup.this;
-                Button selectedButton = radioButtonGroup.getSelection();
-                handled = true;
-                if (keyCode == Keyboard.KeyCode.HOME) {
-                    radioButtonGroup.selectFirstButton();
-                } else if (keyCode == Keyboard.KeyCode.END) {
-                    radioButtonGroup.selectLastButton();
-                } else if (keyCode == Keyboard.KeyCode.LEFT || keyCode == Keyboard.KeyCode.UP) {
-                    radioButtonGroup.selectPreviousButton(selectedButton);
-                } else if (keyCode == Keyboard.KeyCode.RIGHT || keyCode == Keyboard.KeyCode.DOWN) {
-                    radioButtonGroup.selectNextButton(selectedButton);
-                } else {
-                    handled = false;
-                }
-            }
-
-            return handled;
-        }
-
-        /**
-         * Attempt to jump to the button whose rendered text begins with the
-         * typed character.<br/>
-         */
-        @Override
-        public boolean keyTyped(Component component, char character) {
-            int modifiers = Keyboard.getModifiers();
-            boolean handled = false;
-
-            // We are only interested when a key is typed with no modifier, or
-            // just SHIFT (which is used to reverse the search direction)
-            boolean noModifiersPressed = (modifiers == 0);
-            boolean shiftPressed = (modifiers == Modifier.SHIFT.getMask());
-            if (noModifiersPressed || shiftPressed) {
-                RadioButtonGroup radioButtonGroup = RadioButtonGroup.this;
-                Button selectedButton = radioButtonGroup.getSelection();
-
-                firstCharacterFilter.setTarget(character);
-
-                // Determine the starting point for the search
-                int searchStartIndex;
-                if (selectedButton != null) {
-                    searchStartIndex = radioButtonGroup.indexOf(selectedButton);
-                } else {
-                    if (noModifiersPressed) {
-                        searchStartIndex = NO_SELECTION_INDEX;
-                    } else {
-                        searchStartIndex = buttonOrder.getLength();
-                    }
-                }
-
-                int result = NOT_FOUND_INDEX;
-                if (noModifiersPressed) {
-                    result = radioButtonGroup.findNext(searchStartIndex, firstCharacterFilter, true);
-                } else if (shiftPressed) {
-                    result = radioButtonGroup.findPrevious(searchStartIndex, firstCharacterFilter,
-                        true);
-                }
-
-                // Consider the event to have been handled if a different
-                // button end up being selected
-                if (result != NOT_FOUND_INDEX && result != searchStartIndex) {
-                    radioButtonGroup.setSelection(result);
-                    handled = true;
-                }
-            }
-            return handled;
-        }
-    };
-
-    /**
-     * Ensure that all buttons in this group have the custom
-     * ComponentKeyListener.<br/>
-     * This relies on the logic within ButtonGroup to prevent duplicates.
-     */
-    private final ButtonGroupListener buttonGroupListener = new ButtonGroupListener.Adapter() {
-        @Override
-        public void buttonAdded(ButtonGroup buttonGroup, Button button) {
-            button.getComponentKeyListeners().add(componentKeyListener);
-        }
-
-        @Override
-        public void buttonRemoved(ButtonGroup buttonGroup, Button button) {
-            button.getComponentKeyListeners().remove(componentKeyListener);
-        }
-    };
-
-    /**
-     * Filter used to determine selectable buttons within the group
-     */
-    private final Filter<Integer> defaultFilter = new Filter<Integer>() {
-        @Override
-        public boolean include(Integer index) {
-            Button button = buttonOrder.get(index);
-            boolean focusable = button.isFocusable();
-            return focusable;
-        }
-    };
-
-    private final FirstCharacterFilter firstCharacterFilter = new FirstCharacterFilter();
-    private final List<Button> buttonOrder = new ArrayList<Button>();
-    private boolean circular = false;
-    private boolean intraGroupFocusTransferEnabled = false;
-
-    private static final int NOT_FOUND_INDEX = -1;
-    private static final int NO_SELECTION_INDEX = -1;
-
-    public RadioButtonGroup() {
-        getButtonGroupListeners().add(buttonGroupListener);
-    }
-
-    /**
-     * When enabled, a search for the
-     * {@link RadioButtonGroup#selectPreviousButton(Button) previous} or
-     * {@link RadioButtonGroup#selectNextButton(Button) next} focusable button
-     * will not stop when the group's lower or upper bounds (respectively) are
-     * reached.<br/>
-     *
-     * Instead, the search will 'wrap' and continue from the opposite bound
-     * until each button in the entire group has been tested for inclusion.
-     * <br/><br/>
-     *
-     * Defaults to <code>false</code>
-     */
-    public boolean isCircular() {
-        return circular;
-    }
-
-    /**
-     * When enabled, a search for the
-     * {@link RadioButtonGroup#selectPreviousButton(Button) previous} or
-     * {@link RadioButtonGroup#selectNextButton(Button) next} focusable button
-     * will not stop when the group's lower or upper bounds (respectively) are
-     * reached.<br/>
-     *
-     * Instead, the search will 'wrap' and continue from the opposite bound
-     * until each button in the entire group has been tested for inclusion.
-     */
-    public void setCircular(boolean circular) {
-        this.circular = circular;
-    }
-
-    /**
-     * When true, TAB and SHIFT+TAB transfer focus out of the RadioButtonGroup.
-     * <br/>
-     * Defaults to <code>false</code>
-     */
-    public boolean isIntraGroupFocusTransferEnabled() {
-        return intraGroupFocusTransferEnabled;
-    }
-
-    /**
-     * Controls whether TAB and SHIFT+TAB will transfer focus out of the
-     * RadioButtonGroup, or simply maintain their default behaviour.
-     */
-    public void setIntraGroupFocusTransferEnabled(boolean intraGroupFocusTransferEnabled) {
-        this.intraGroupFocusTransferEnabled = intraGroupFocusTransferEnabled;
-    }
-
-    /**
-     * Add a button to the group.
-     *
-     * @see Group#add(Object)
-     * @see Sequence#add(Object)
-     */
-    @Override
-    public boolean add(Button button) {
-        if (button == null) {
-            throw new IllegalArgumentException("Button cannot be null");
-        }
-        boolean result = super.add(button);
-        if (result) {
-            buttonOrder.add(button);
-        }
-        return result;
-    }
-
-    /**
-     * Return the button at the specified index.
-     *
-     * @see Sequence#get(int)
-     */
-    public Button get(int index) {
-        return buttonOrder.get(index);
-    }
-
-    /**
-     * Return the number of buttons in the group.
-     *
-     * @see Sequence#getLength()
-     */
-    public int getLength() {
-        return buttonOrder.getLength();
-    }
-
-    /**
-     * Return the index (order) of the button within the group.
-     *
-     * @return The index or -1 if the button does not belong to this
-     * RadioButtonGroup
-     *
-     * @see Sequence#indexOf(Object)
-     */
-    public int indexOf(Button button) {
-        return buttonOrder.indexOf(button);
-    }
-
-    /**
-     * Insert a button at the specified index.
-     *
-     * @see Sequence#insert(Object, int)
-     */
-    public void insert(Button button, int index) {
-        if (button == null) {
-            throw new IllegalArgumentException("Button cannot be null");
-        }
-        boolean result = super.add(button);
-        if (result) {
-            buttonOrder.insert(button, index);
-        }
-    }
-
-    /**
-     * Remove the button from the group.
-     *
-     * @see Group#remove(Object)
-     * @see Sequence#remove(Object)
-     */
-    @Override
-    public boolean remove(Button button) {
-        boolean result = false;
-        if (button != null) {
-            result = super.remove(button);
-            if (result) {
-                buttonOrder.remove(button);
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Remove <code>count</code> buttons from the group starting at
-     * <code>index</code>.
-     *
-     * @see Sequence#remove(int, int)
-     */
-    public Sequence<Button> remove(int index, int count) {
-        Sequence<Button> removed = new ArrayList<Button>();
-        while (count-- > 0) {
-            Button button = get(index);
-            boolean result = this.remove(button);
-            if (result) {
-                removed.add(button);
-            }
-        }
-        return removed;
-    }
-
-
-    /**
-     * Return an iterator for the <strong>ordered</strong> list of buttons
-     */
-    @Override
-    public Iterator<Button> iterator() {
-        return new ImmutableIterator<Button>(buttonOrder.iterator());
-    }
-
-    /**
-     * Select and <strong>focus</strong> the specified button.
-     *
-     * @see ButtonGroup#setSelection(Button)
-     */
-    @Override
-    public void setSelection(Button button) {
-        super.setSelection(button);
-        if (button != null) {
-            button.requestFocus();
-        }
-    }
-
-    /**
-     * Select and <strong>focus</strong> the button at the specified index,
-     * unless the index is NOT_FOUND_INDEX.
-     */
-    public void setSelection(int index) {
-        if (index != NOT_FOUND_INDEX) {
-            this.setSelection(buttonOrder.get(index));
-        }
-    }
-
-    /**
-     * Select the first focusable button in the group.
-     */
-    public void selectFirstButton() {
-        setSelection(findNext(NO_SELECTION_INDEX));
-    }
-
-    /**
-     * Select the last focusable button in the group.
-     */
-    public void selectLastButton() {
-        setSelection(findPrevious(buttonOrder.getLength()));
-    }
-
-    /**
-     * Working forwards from the specified button, select the first focusable
-     * button.
-     *
-     * @param button If null, the first available button will be selected,
-     * unless the group contains a selected or focused button, in which case
-     * that button will be used as the starting point for the search.
-     */
-    public void selectNextButton(Button button) {
-        // No explicit starting point was supplied
-        if (button == null) {
-            // If there is a selected button in this group, we will try to use
-            // it as the starting point.
-            button = getSelection();
-
-            if (button == null) {
-                // No selection, but perhaps one of the buttons has focus?
-                Component focusedComponent = Component.getFocusedComponent();
-                if (focusedComponent instanceof Button) {
-                    int index = this.indexOf((Button)focusedComponent);
-                    if (index != NOT_FOUND_INDEX) {
-                        button = this.get(index);
-                    }
-                }
-            }
-            // Try again, using new starting point if one was determined
-            if (button != null) {
-                selectNextButton(button);
-            } else {
-                selectFirstButton();
-            }
-        } else {
-            int index = indexOf(button);
-            if (index == NOT_FOUND_INDEX) {
-                throw new IllegalArgumentException(
-                    "Button does not belong to this RadioButtonGroup.");
-            }
-            index = findNext(index);
-            if (index != NOT_FOUND_INDEX) {
-                setSelection(index);
-            }
-        }
-    }
-
-    /**
-     * Working backwards from the specified button, select the first focusable
-     * button.
-     *
-     * @param button If null, the last available button will be selected, unless
-     * the group contains a selected or focused button, in which case that
-     * button will be used as the starting point for the search.
-     */
-    public void selectPreviousButton(Button button) {
-        // No explicit starting point was supplied
-        if (button == null) {
-            // If there is a selected button in this group, we will try to use
-            // it as the starting point.
-            button = getSelection();
-
-            if (button == null) {
-                // No selection, but perhaps one of the buttons has focus?
-                Component focusedComponent = Component.getFocusedComponent();
-                if (focusedComponent instanceof Button) {
-                    int index = this.indexOf((Button)focusedComponent);
-                    if (index != NOT_FOUND_INDEX) {
-                        button = this.get(index);
-                    }
-                }
-            }
-
-            // Try again, using new starting point if one was determined
-            if (button != null) {
-                selectPreviousButton(button);
-            } else {
-                selectLastButton();
-            }
-        } else {
-            int index = indexOf(button);
-            if (index == NOT_FOUND_INDEX) {
-                throw new IllegalArgumentException(
-                    "Button does not belong to this RadioButtonGroup.");
-            }
-            index = findPrevious(index);
-            if (index != NOT_FOUND_INDEX) {
-                setSelection(index);
-            }
-        }
-    }
-
-    /**
-     * Iterate forwards over the buttons in the group, looping back to the start
-     * if the upper bound is reached and the <code>circular</code> parameter is
-     * true.
-     *
-     * @param index Index to which the 'next' is relative
-     * @param filter Alternative filter to use during the search.
-     * @param circular Loop when upper bound is reached
-     * @return The first button found to satisfy the filter
-     *
-     * @see #setCircular(boolean)
-     */
-    private int findNext(int index, Filter<Integer> filter, boolean circular) {
-        filter = (filter == null ? defaultFilter : filter);
-        int result = NOT_FOUND_INDEX;
-        int length = buttonOrder.getLength();
-        if (length > 0) {
-            // (index + 1) --> last index
-            for (int i = (index + 1); i < length; i++) {
-                if (filter.include(i)) {
-                    result = i;
-                    break;
-                }
-            }
-            if (circular && result == NOT_FOUND_INDEX) {
-                // first index --> index
-                for (int i = 0; i <= index; i++) {
-                    if (filter.include(i)) {
-                        result = i;
-                        break;
-                    }
-                }
-            }
-        }
-        return result;
-    }
-
-    private int findNext(int index) {
-        return findNext(index, defaultFilter, circular);
-    }
-
-    /**
-     * Iterate backwards over the buttons in the group, looping back to the end
-     * if the lower bound is reached and the <code>circular</code> parameter is
-     * true.
-     *
-     * @param index Index to which the 'previous' is relative
-     * @param filter Alternative filter to use during the search.
-     * @param circular Loop when lower bound is reached
-     * @return The first focusable button found
-     *
-     * @see #setCircular(boolean)
-     */
-    private int findPrevious(int index, Filter<Integer> filter, boolean circular) {
-        filter = (filter == null ? defaultFilter : filter);
-        int result = NOT_FOUND_INDEX;
-        int length = buttonOrder.getLength();
-        if (length > 0) {
-            // (index - 1) --> first index
-            for (int i = (index - 1); i >= 0; i--) {
-                if (filter.include(i)) {
-                    result = i;
-                    break;
-                }
-            }
-            if (circular && result == NOT_FOUND_INDEX) {
-                // last index --> index
-                for (int i = (length - 1); i >= index; i--) {
-                    if (filter.include(i)) {
-                        result = i;
-                        break;
-                    }
-                }
-            }
-        }
-        return result;
-    }
-
-    private int findPrevious(int index) {
-        return findPrevious(index, defaultFilter, circular);
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk;
+
+import java.util.Iterator;
+
+import org.apache.pivot.collections.ArrayList;
+import org.apache.pivot.collections.Group;
+import org.apache.pivot.collections.List;
+import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.util.Filter;
+import org.apache.pivot.util.ImmutableIterator;
+import org.apache.pivot.wtk.Keyboard.KeyCode;
+import org.apache.pivot.wtk.Keyboard.KeyLocation;
+import org.apache.pivot.wtk.Keyboard.Modifier;
+
+/**
+ * Extension of {@link ButtonGroup} providing keyboard navigation within the
+ * group and modified focus navigation that treats the group as a single
+ * focusable entity.<br/><br/>
+ *
+ * {@link KeyCode#UP UP} & {@link KeyCode#LEFT LEFT} Select the previous
+ * button<br/>
+ * {@link KeyCode#DOWN DOWN} & {@link KeyCode#RIGHT RIGHT} Select the next
+ * button<br/>
+ * {@link KeyCode#HOME HOME} Select the first button<br/>
+ * {@link KeyCode#END END} Select the last button<br/><br/>
+ *
+ * (Note that only {@link Component#isFocusable() focusable} buttons are
+ * considered when searching for a Button to select)<br/><br/>
+ *
+ * When a button within the group is focused and key is typed, an attempt is
+ * made to find the next button (default) or previous button (when the SHIFT
+ * modifier is pressed) whose renderer text starts with the typed character.
+ * This search will always behave as if the <code>circular</code> property were
+ * set.<br/><br/>
+ *
+ * By default, {@link KeyCode#TAB TAB} and {@link KeyCode#TAB SHIFT+TAB}
+ * key presses will transfer focus out of the group (forwards or backwards
+ * respectively).
+ * This is managed by the {@link #setIntraGroupFocusTransferEnabled(boolean)
+ * intraGroupFocusTransferEnabled} property.<br/><br/>
+ *
+ * The {@link #setCircular(boolean) circular} property can be enabled to allow
+ * the selection to transfer seamlessly from one end of the group to the other.
+ * (i.e. holding down an arrow key will cycle through all focusable buttons)
+ * <br/><br/>
+ *
+ * Note that due to the conflicting return types of the <code>add(T)</code> and
+ * <code>remove(T)</code> methods in the {@link Group#add(Object) Group} and
+ * {@link Sequence#add(Object) Sequence} interfaces, this class cannot actually
+ * implement <code>Sequence&lt;Button&gt;</code>, although most of the same
+ * methods are implemented.<br/>
+ */
+public class RadioButtonGroup extends ButtonGroup {
+
+    /**
+     * Filter used to determine selectable buttons whose rendered data starts
+     * with the target character.
+     */
+    private class FirstCharacterFilter implements Filter<Integer> {
+        private char target = '\0';
+
+        public void setTarget(char target) {
+            this.target = Character.toUpperCase(target);
+        }
+
+        @Override
+        public boolean include(Integer index) {
+            boolean include = defaultFilter.include(index);
+            if (include) {
+                Button button = buttonOrder.get(index);
+                String rendered = button.getDataRenderer().toString(button.getButtonData());
+                if (rendered != null && rendered.length() > 0) {
+                    char first = Character.toUpperCase(rendered.charAt(0));
+                    if (first != target) {
+                        include = false;
+                    }
+                }
+            }
+            return include;
+        }
+    }
+
+    /**
+     * ComponentKeyListener to be applied to all buttons as they are added to
+     * the group.<br/><br/>
+     *
+     * At least one button in the group must be focused for this listener to be
+     * executed, but that won't necessarily be a selected button.<br/>
+     * This also means that the group will not be empty, although some of the
+     * buttons contained within may not be focusable, or even visible.
+     */
+    private final ComponentKeyListener componentKeyListener = new ComponentKeyListener.Adapter() {
+        /**
+         * Handle TAB & SHIFT+TAB focus traversal, HOME, END & arrow keys
+         */
+        @Override
+        public boolean keyPressed(Component component, int keyCode, KeyLocation keyLocation) {
+            int modifiers = Keyboard.getModifiers();
+            boolean handled = false;
+
+            /*
+             * Potentially transfer focus away from the buttons in this group.
+             *
+             * At this point we know that at least one button is focused, so we
+             * just need to find the first or last (and possibly only) focusable
+             * button depending on the focus transfer direction and then
+             * transfer away from it.
+             */
+            if (!intraGroupFocusTransferEnabled) {
+                if (keyCode == KeyCode.TAB) {
+                    if (modifiers == 0) {
+                        Button lastFocusableButton = get(findPrevious(buttonOrder.getLength()));
+                        lastFocusableButton.transferFocus(FocusTraversalDirection.FORWARD);
+                        handled = true;
+                    } else if (modifiers == Modifier.SHIFT.getMask()) {
+                        Button firstFocusableButton = get(findNext(NO_SELECTION_INDEX));
+                        firstFocusableButton.transferFocus(FocusTraversalDirection.BACKWARD);
+                        handled = true;
+                    }
+                }
+            }
+
+            // Navigation/selection within the group
+            if (!handled && modifiers == 0) {
+                RadioButtonGroup radioButtonGroup = RadioButtonGroup.this;
+                Button selectedButton = radioButtonGroup.getSelection();
+                handled = true;
+                if (keyCode == Keyboard.KeyCode.HOME) {
+                    radioButtonGroup.selectFirstButton();
+                } else if (keyCode == Keyboard.KeyCode.END) {
+                    radioButtonGroup.selectLastButton();
+                } else if (keyCode == Keyboard.KeyCode.LEFT || keyCode == Keyboard.KeyCode.UP) {
+                    radioButtonGroup.selectPreviousButton(selectedButton);
+                } else if (keyCode == Keyboard.KeyCode.RIGHT || keyCode == Keyboard.KeyCode.DOWN) {
+                    radioButtonGroup.selectNextButton(selectedButton);
+                } else {
+                    handled = false;
+                }
+            }
+
+            return handled;
+        }
+
+        /**
+         * Attempt to jump to the button whose rendered text begins with the
+         * typed character.<br/>
+         */
+        @Override
+        public boolean keyTyped(Component component, char character) {
+            int modifiers = Keyboard.getModifiers();
+            boolean handled = false;
+
+            // We are only interested when a key is typed with no modifier, or
+            // just SHIFT (which is used to reverse the search direction)
+            boolean noModifiersPressed = (modifiers == 0);
+            boolean shiftPressed = (modifiers == Modifier.SHIFT.getMask());
+            if (noModifiersPressed || shiftPressed) {
+                RadioButtonGroup radioButtonGroup = RadioButtonGroup.this;
+                Button selectedButton = radioButtonGroup.getSelection();
+
+                firstCharacterFilter.setTarget(character);
+
+                // Determine the starting point for the search
+                int searchStartIndex;
+                if (selectedButton != null) {
+                    searchStartIndex = radioButtonGroup.indexOf(selectedButton);
+                } else {
+                    if (noModifiersPressed) {
+                        searchStartIndex = NO_SELECTION_INDEX;
+                    } else {
+                        searchStartIndex = buttonOrder.getLength();
+                    }
+                }
+
+                int result = NOT_FOUND_INDEX;
+                if (noModifiersPressed) {
+                    result = radioButtonGroup.findNext(searchStartIndex, firstCharacterFilter, true);
+                } else if (shiftPressed) {
+                    result = radioButtonGroup.findPrevious(searchStartIndex, firstCharacterFilter,
+                        true);
+                }
+
+                // Consider the event to have been handled if a different
+                // button end up being selected
+                if (result != NOT_FOUND_INDEX && result != searchStartIndex) {
+                    radioButtonGroup.setSelection(result);
+                    handled = true;
+                }
+            }
+            return handled;
+        }
+    };
+
+    /**
+     * Ensure that all buttons in this group have the custom
+     * ComponentKeyListener.<br/>
+     * This relies on the logic within ButtonGroup to prevent duplicates.
+     */
+    private final ButtonGroupListener buttonGroupListener = new ButtonGroupListener.Adapter() {
+        @Override
+        public void buttonAdded(ButtonGroup buttonGroup, Button button) {
+            button.getComponentKeyListeners().add(componentKeyListener);
+        }
+
+        @Override
+        public void buttonRemoved(ButtonGroup buttonGroup, Button button) {
+            button.getComponentKeyListeners().remove(componentKeyListener);
+        }
+    };
+
+    /**
+     * Filter used to determine selectable buttons within the group
+     */
+    private final Filter<Integer> defaultFilter = new Filter<Integer>() {
+        @Override
+        public boolean include(Integer index) {
+            Button button = buttonOrder.get(index);
+            boolean focusable = button.isFocusable();
+            return focusable;
+        }
+    };
+
+    private final FirstCharacterFilter firstCharacterFilter = new FirstCharacterFilter();
+    private final List<Button> buttonOrder = new ArrayList<Button>();
+    private boolean circular = false;
+    private boolean intraGroupFocusTransferEnabled = false;
+
+    private static final int NOT_FOUND_INDEX = -1;
+    private static final int NO_SELECTION_INDEX = -1;
+
+    public RadioButtonGroup() {
+        getButtonGroupListeners().add(buttonGroupListener);
+    }
+
+    /**
+     * When enabled, a search for the
+     * {@link RadioButtonGroup#selectPreviousButton(Button) previous} or
+     * {@link RadioButtonGroup#selectNextButton(Button) next} focusable button
+     * will not stop when the group's lower or upper bounds (respectively) are
+     * reached.<br/>
+     *
+     * Instead, the search will 'wrap' and continue from the opposite bound
+     * until each button in the entire group has been tested for inclusion.
+     * <br/><br/>
+     *
+     * Defaults to <code>false</code>
+     */
+    public boolean isCircular() {
+        return circular;
+    }
+
+    /**
+     * When enabled, a search for the
+     * {@link RadioButtonGroup#selectPreviousButton(Button) previous} or
+     * {@link RadioButtonGroup#selectNextButton(Button) next} focusable button
+     * will not stop when the group's lower or upper bounds (respectively) are
+     * reached.<br/>
+     *
+     * Instead, the search will 'wrap' and continue from the opposite bound
+     * until each button in the entire group has been tested for inclusion.
+     */
+    public void setCircular(boolean circular) {
+        this.circular = circular;
+    }
+
+    /**
+     * When true, TAB and SHIFT+TAB transfer focus out of the RadioButtonGroup.
+     * <br/>
+     * Defaults to <code>false</code>
+     */
+    public boolean isIntraGroupFocusTransferEnabled() {
+        return intraGroupFocusTransferEnabled;
+    }
+
+    /**
+     * Controls whether TAB and SHIFT+TAB will transfer focus out of the
+     * RadioButtonGroup, or simply maintain their default behaviour.
+     */
+    public void setIntraGroupFocusTransferEnabled(boolean intraGroupFocusTransferEnabled) {
+        this.intraGroupFocusTransferEnabled = intraGroupFocusTransferEnabled;
+    }
+
+    /**
+     * Add a button to the group.
+     *
+     * @see Group#add(Object)
+     * @see Sequence#add(Object)
+     */
+    @Override
+    public boolean add(Button button) {
+        if (button == null) {
+            throw new IllegalArgumentException("Button cannot be null");
+        }
+        boolean result = super.add(button);
+        if (result) {
+            buttonOrder.add(button);
+        }
+        return result;
+    }
+
+    /**
+     * Return the button at the specified index.
+     *
+     * @see Sequence#get(int)
+     */
+    public Button get(int index) {
+        return buttonOrder.get(index);
+    }
+
+    /**
+     * Return the number of buttons in the group.
+     *
+     * @see Sequence#getLength()
+     */
+    public int getLength() {
+        return buttonOrder.getLength();
+    }
+
+    /**
+     * Return the index (order) of the button within the group.
+     *
+     * @return The index or -1 if the button does not belong to this
+     * RadioButtonGroup
+     *
+     * @see Sequence#indexOf(Object)
+     */
+    public int indexOf(Button button) {
+        return buttonOrder.indexOf(button);
+    }
+
+    /**
+     * Insert a button at the specified index.
+     *
+     * @see Sequence#insert(Object, int)
+     */
+    public void insert(Button button, int index) {
+        if (button == null) {
+            throw new IllegalArgumentException("Button cannot be null");
+        }
+        boolean result = super.add(button);
+        if (result) {
+            buttonOrder.insert(button, index);
+        }
+    }
+
+    /**
+     * Remove the button from the group.
+     *
+     * @see Group#remove(Object)
+     * @see Sequence#remove(Object)
+     */
+    @Override
+    public boolean remove(Button button) {
+        boolean result = false;
+        if (button != null) {
+            result = super.remove(button);
+            if (result) {
+                buttonOrder.remove(button);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Remove <code>count</code> buttons from the group starting at
+     * <code>index</code>.
+     *
+     * @see Sequence#remove(int, int)
+     */
+    public Sequence<Button> remove(int index, int count) {
+        Sequence<Button> removed = new ArrayList<Button>();
+        while (count-- > 0) {
+            Button button = get(index);
+            boolean result = this.remove(button);
+            if (result) {
+                removed.add(button);
+            }
+        }
+        return removed;
+    }
+
+
+    /**
+     * Return an iterator for the <strong>ordered</strong> list of buttons
+     */
+    @Override
+    public Iterator<Button> iterator() {
+        return new ImmutableIterator<Button>(buttonOrder.iterator());
+    }
+
+    /**
+     * Select and <strong>focus</strong> the specified button.
+     *
+     * @see ButtonGroup#setSelection(Button)
+     */
+    @Override
+    public void setSelection(Button button) {
+        super.setSelection(button);
+        if (button != null) {
+            button.requestFocus();
+        }
+    }
+
+    /**
+     * Select and <strong>focus</strong> the button at the specified index,
+     * unless the index is NOT_FOUND_INDEX.
+     */
+    public void setSelection(int index) {
+        if (index != NOT_FOUND_INDEX) {
+            this.setSelection(buttonOrder.get(index));
+        }
+    }
+
+    /**
+     * Select the first focusable button in the group.
+     */
+    public void selectFirstButton() {
+        setSelection(findNext(NO_SELECTION_INDEX));
+    }
+
+    /**
+     * Select the last focusable button in the group.
+     */
+    public void selectLastButton() {
+        setSelection(findPrevious(buttonOrder.getLength()));
+    }
+
+    /**
+     * Working forwards from the specified button, select the first focusable
+     * button.
+     *
+     * @param button If null, the first available button will be selected,
+     * unless the group contains a selected or focused button, in which case
+     * that button will be used as the starting point for the search.
+     */
+    public void selectNextButton(Button button) {
+        // No explicit starting point was supplied
+        if (button == null) {
+            // If there is a selected button in this group, we will try to use
+            // it as the starting point.
+            button = getSelection();
+
+            if (button == null) {
+                // No selection, but perhaps one of the buttons has focus?
+                Component focusedComponent = Component.getFocusedComponent();
+                if (focusedComponent instanceof Button) {
+                    int index = this.indexOf((Button)focusedComponent);
+                    if (index != NOT_FOUND_INDEX) {
+                        button = this.get(index);
+                    }
+                }
+            }
+            // Try again, using new starting point if one was determined
+            if (button != null) {
+                selectNextButton(button);
+            } else {
+                selectFirstButton();
+            }
+        } else {
+            int index = indexOf(button);
+            if (index == NOT_FOUND_INDEX) {
+                throw new IllegalArgumentException(
+                    "Button does not belong to this RadioButtonGroup.");
+            }
+            index = findNext(index);
+            if (index != NOT_FOUND_INDEX) {
+                setSelection(index);
+            }
+        }
+    }
+
+    /**
+     * Working backwards from the specified button, select the first focusable
+     * button.
+     *
+     * @param button If null, the last available button will be selected, unless
+     * the group contains a selected or focused button, in which case that
+     * button will be used as the starting point for the search.
+     */
+    public void selectPreviousButton(Button button) {
+        // No explicit starting point was supplied
+        if (button == null) {
+            // If there is a selected button in this group, we will try to use
+            // it as the starting point.
+            button = getSelection();
+
+            if (button == null) {
+                // No selection, but perhaps one of the buttons has focus?
+                Component focusedComponent = Component.getFocusedComponent();
+                if (focusedComponent instanceof Button) {
+                    int index = this.indexOf((Button)focusedComponent);
+                    if (index != NOT_FOUND_INDEX) {
+                        button = this.get(index);
+                    }
+                }
+            }
+
+            // Try again, using new starting point if one was determined
+            if (button != null) {
+                selectPreviousButton(button);
+            } else {
+                selectLastButton();
+            }
+        } else {
+            int index = indexOf(button);
+            if (index == NOT_FOUND_INDEX) {
+                throw new IllegalArgumentException(
+                    "Button does not belong to this RadioButtonGroup.");
+            }
+            index = findPrevious(index);
+            if (index != NOT_FOUND_INDEX) {
+                setSelection(index);
+            }
+        }
+    }
+
+    /**
+     * Iterate forwards over the buttons in the group, looping back to the start
+     * if the upper bound is reached and the <code>circular</code> parameter is
+     * true.
+     *
+     * @param index Index to which the 'next' is relative
+     * @param filter Alternative filter to use during the search.
+     * @param circular Loop when upper bound is reached
+     * @return The first button found to satisfy the filter
+     *
+     * @see #setCircular(boolean)
+     */
+    private int findNext(int index, Filter<Integer> filter, boolean circular) {
+        filter = (filter == null ? defaultFilter : filter);
+        int result = NOT_FOUND_INDEX;
+        int length = buttonOrder.getLength();
+        if (length > 0) {
+            // (index + 1) --> last index
+            for (int i = (index + 1); i < length; i++) {
+                if (filter.include(i)) {
+                    result = i;
+                    break;
+                }
+            }
+            if (circular && result == NOT_FOUND_INDEX) {
+                // first index --> index
+                for (int i = 0; i <= index; i++) {
+                    if (filter.include(i)) {
+                        result = i;
+                        break;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    private int findNext(int index) {
+        return findNext(index, defaultFilter, circular);
+    }
+
+    /**
+     * Iterate backwards over the buttons in the group, looping back to the end
+     * if the lower bound is reached and the <code>circular</code> parameter is
+     * true.
+     *
+     * @param index Index to which the 'previous' is relative
+     * @param filter Alternative filter to use during the search.
+     * @param circular Loop when lower bound is reached
+     * @return The first focusable button found
+     *
+     * @see #setCircular(boolean)
+     */
+    private int findPrevious(int index, Filter<Integer> filter, boolean circular) {
+        filter = (filter == null ? defaultFilter : filter);
+        int result = NOT_FOUND_INDEX;
+        int length = buttonOrder.getLength();
+        if (length > 0) {
+            // (index - 1) --> first index
+            for (int i = (index - 1); i >= 0; i--) {
+                if (filter.include(i)) {
+                    result = i;
+                    break;
+                }
+            }
+            if (circular && result == NOT_FOUND_INDEX) {
+                // last index --> index
+                for (int i = (length - 1); i >= index; i--) {
+                    if (filter.include(i)) {
+                        result = i;
+                        break;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    private int findPrevious(int index) {
+        return findPrevious(index, defaultFilter, circular);
+    }
+}

Propchange: pivot/trunk/wtk/src/org/apache/pivot/wtk/RadioButtonGroup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/WTKListenerList.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/WTKListenerList.java?rev=1222994&r1=1222993&r2=1222994&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/WTKListenerList.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/WTKListenerList.java Sat Dec 24 16:07:37 2011
@@ -1,26 +1,26 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you 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 org.apache.pivot.wtk;
-
-import org.apache.pivot.util.ListenerList;
-
-/**
- * This is a customised subclass of ListenerList that adds thread-safety checks
- * for the WTK components.
- */
-public class WTKListenerList<T> extends ListenerList<T> {
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk;
+
+import org.apache.pivot.util.ListenerList;
+
+/**
+ * This is a customised subclass of ListenerList that adds thread-safety checks
+ * for the WTK components.
+ */
+public class WTKListenerList<T> extends ListenerList<T> {
+}

Propchange: pivot/trunk/wtk/src/org/apache/pivot/wtk/WTKListenerList.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewCheckboxCellRenderer.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewCheckboxCellRenderer.java?rev=1222994&r1=1222993&r2=1222994&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewCheckboxCellRenderer.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewCheckboxCellRenderer.java Sat Dec 24 16:07:37 2011
@@ -1,83 +1,83 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you 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 org.apache.pivot.wtk.content;
-
-import org.apache.pivot.wtk.Button;
-import org.apache.pivot.wtk.Checkbox;
-import org.apache.pivot.wtk.BoxPane;
-import org.apache.pivot.wtk.HorizontalAlignment;
-import org.apache.pivot.wtk.TableView;
-import org.apache.pivot.wtk.VerticalAlignment;
-
-/**
- * Default renderer for table view cells that contain boolean data. Renders
- * cell contents as a checkbox.
- */
-public class TableViewCheckboxCellRenderer extends BoxPane
-    implements TableView.CellRenderer {
-    protected Checkbox checkbox = new Checkbox();
-    private boolean checkboxDisabled = false;
-
-    public TableViewCheckboxCellRenderer() {
-        add(checkbox);
-
-        getStyles().put("padding", 3);
-        getStyles().put("verticalAlignment", VerticalAlignment.CENTER);
-        getStyles().put("horizontalAlignment", HorizontalAlignment.CENTER);
-    }
-
-    @Override
-    public void setSize(int width, int height) {
-        super.setSize(width, height);
-
-        // Since this component doesn't have a parent, it won't be validated
-        // via layout; ensure that it is valid here
-        validate();
-    }
-
-    @Override
-    public void render(Object row, int rowIndex, int columnIndex,
-        TableView tableView, String columnName,
-        boolean selected, boolean highlighted, boolean disabled) {
-        if (row != null) {
-            // Get the row and cell data
-            if (columnName != null) {
-                if (checkbox.isTriState()) {
-                    checkbox.setStateKey(columnName);
-                } else {
-                    checkbox.setSelectedKey(columnName);
-                }
-                checkbox.load(row);
-            } else {
-                checkbox.setState(Button.State.UNSELECTED);
-            }
-            checkbox.setEnabled(!checkboxDisabled && tableView.isEnabled() && !disabled);
-        }
-    }
-
-    public String toString(Object row, String columnName) {
-        return null;
-    }
-
-    public boolean isCheckboxDisabled() {
-        return checkboxDisabled;
-    }
-
-    public void setCheckboxDisabled(boolean checkboxDisabled) {
-        this.checkboxDisabled = checkboxDisabled;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk.content;
+
+import org.apache.pivot.wtk.Button;
+import org.apache.pivot.wtk.Checkbox;
+import org.apache.pivot.wtk.BoxPane;
+import org.apache.pivot.wtk.HorizontalAlignment;
+import org.apache.pivot.wtk.TableView;
+import org.apache.pivot.wtk.VerticalAlignment;
+
+/**
+ * Default renderer for table view cells that contain boolean data. Renders
+ * cell contents as a checkbox.
+ */
+public class TableViewCheckboxCellRenderer extends BoxPane
+    implements TableView.CellRenderer {
+    protected Checkbox checkbox = new Checkbox();
+    private boolean checkboxDisabled = false;
+
+    public TableViewCheckboxCellRenderer() {
+        add(checkbox);
+
+        getStyles().put("padding", 3);
+        getStyles().put("verticalAlignment", VerticalAlignment.CENTER);
+        getStyles().put("horizontalAlignment", HorizontalAlignment.CENTER);
+    }
+
+    @Override
+    public void setSize(int width, int height) {
+        super.setSize(width, height);
+
+        // Since this component doesn't have a parent, it won't be validated
+        // via layout; ensure that it is valid here
+        validate();
+    }
+
+    @Override
+    public void render(Object row, int rowIndex, int columnIndex,
+        TableView tableView, String columnName,
+        boolean selected, boolean highlighted, boolean disabled) {
+        if (row != null) {
+            // Get the row and cell data
+            if (columnName != null) {
+                if (checkbox.isTriState()) {
+                    checkbox.setStateKey(columnName);
+                } else {
+                    checkbox.setSelectedKey(columnName);
+                }
+                checkbox.load(row);
+            } else {
+                checkbox.setState(Button.State.UNSELECTED);
+            }
+            checkbox.setEnabled(!checkboxDisabled && tableView.isEnabled() && !disabled);
+        }
+    }
+
+    public String toString(Object row, String columnName) {
+        return null;
+    }
+
+    public boolean isCheckboxDisabled() {
+        return checkboxDisabled;
+    }
+
+    public void setCheckboxDisabled(boolean checkboxDisabled) {
+        this.checkboxDisabled = checkboxDisabled;
+    }
+}

Propchange: pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewCheckboxCellRenderer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewTriStateCellRenderer.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewTriStateCellRenderer.java?rev=1222994&r1=1222993&r2=1222994&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewTriStateCellRenderer.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewTriStateCellRenderer.java Sat Dec 24 16:07:37 2011
@@ -1,29 +1,29 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you 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 org.apache.pivot.wtk.content;
-
-/**
- * Default renderer for table view cells that contain tri-state data. Renders
- * cell contents as a tri-state checkbox.
- */
-public class TableViewTriStateCellRenderer extends TableViewCheckboxCellRenderer
-{
-    public TableViewTriStateCellRenderer() {
-        super();
-        checkbox.setTriState(true);
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk.content;
+
+/**
+ * Default renderer for table view cells that contain tri-state data. Renders
+ * cell contents as a tri-state checkbox.
+ */
+public class TableViewTriStateCellRenderer extends TableViewCheckboxCellRenderer
+{
+    public TableViewTriStateCellRenderer() {
+        super();
+        checkbox.setTriState(true);
+    }
+}

Propchange: pivot/trunk/wtk/src/org/apache/pivot/wtk/content/TableViewTriStateCellRenderer.java
------------------------------------------------------------------------------
    svn:eol-style = native