All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.pivot.wtk.ListButton Maven / Gradle / Ivy

The newest version!
/*
 * 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.io.IOException;
import java.net.URL;
import java.util.Comparator;

import org.apache.pivot.beans.DefaultProperty;
import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.List;
import org.apache.pivot.collections.ListListener;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.json.JSON;
import org.apache.pivot.json.JSONSerializer;
import org.apache.pivot.serialization.SerializationException;
import org.apache.pivot.util.Filter;
import org.apache.pivot.util.ListenerList;
import org.apache.pivot.wtk.ListView.ListDataBindMapping;
import org.apache.pivot.wtk.content.ListButtonDataRenderer;
import org.apache.pivot.wtk.content.ListViewItemRenderer;

/**
 * Component that allows a user to select one of several list options. The
 * options are hidden until the user pushes the button.
 */
@DefaultProperty("listData")
public class ListButton extends Button {
    private static class ListButtonListenerList extends WTKListenerList
        implements ListButtonListener {
        @Override
        public void listDataChanged(ListButton listButton, List previousListData) {
            for (ListButtonListener listener : this) {
                listener.listDataChanged(listButton, previousListData);
            }
        }

        @Override
        public void itemRendererChanged(ListButton listButton, ListView.ItemRenderer previousItemRenderer) {
            for (ListButtonListener listener : this) {
                listener.itemRendererChanged(listButton, previousItemRenderer);
            }
        }

        @Override
        public void repeatableChanged(ListButton listButton) {
            for (ListButtonListener listener : this) {
                listener.repeatableChanged(listButton);
            }
        }

        @Override
        public void disabledItemFilterChanged(ListButton listButton, Filter previousDisabledItemFilter) {
            for (ListButtonListener listener : this) {
                listener.disabledItemFilterChanged(listButton, previousDisabledItemFilter);
            }
        }

        @Override
        public void listSizeChanged(ListButton listButton, int previousListSize) {
            for (ListButtonListener listener : this) {
                listener.listSizeChanged(listButton, previousListSize);
            }
        }
    }

    private static class ListButtonItemListenerList extends WTKListenerList
        implements ListButtonItemListener {
        @Override
        public void itemInserted(ListButton listButton, int index) {
            for (ListButtonItemListener listener : this) {
                listener.itemInserted(listButton, index);
            }
        }

        @Override
        public void itemsRemoved(ListButton listButton, int index, int count) {
            for (ListButtonItemListener listener : this) {
                listener.itemsRemoved(listButton, index, count);
            }
        }

        @Override
        public void itemUpdated(ListButton listButton, int index) {
            for (ListButtonItemListener listener : this) {
                listener.itemUpdated(listButton, index);
            }
        }

        @Override
        public void itemsCleared(ListButton listButton) {
            for (ListButtonItemListener listener : this) {
                listener.itemsCleared(listButton);
            }
        }

        @Override
        public void itemsSorted(ListButton listButton) {
            for (ListButtonItemListener listener : this) {
                listener.itemsSorted(listButton);
            }
        }
    }

    private static class ListButtonSelectionListenerList extends WTKListenerList
        implements ListButtonSelectionListener {
        @Override
        public void selectedIndexChanged(ListButton listButton, int previousSelectedIndex) {
            for (ListButtonSelectionListener listener : this) {
                listener.selectedIndexChanged(listButton, previousSelectedIndex);
            }
        }

        @Override
        public void selectedItemChanged(ListButton listButton, Object previousSelectedItem) {
            for (ListButtonSelectionListener listener : this) {
                listener.selectedItemChanged(listButton, previousSelectedItem);
            }
        }
    }

    private static class ListButtonBindingListenerList extends WTKListenerList
        implements ListButtonBindingListener {
        @Override
        public void listDataKeyChanged(ListButton listButton, String previousListDataKey) {
            for (ListButtonBindingListener listener : this) {
                listener.listDataKeyChanged(listButton, previousListDataKey);
            }
        }

        @Override
        public void listDataBindTypeChanged(ListButton listButton, BindType previousListDataBindType) {
            for (ListButtonBindingListener listener : this) {
                listener.listDataBindTypeChanged(listButton, previousListDataBindType);
            }
        }

        @Override
        public void listDataBindMappingChanged(ListButton listButton,
            ListView.ListDataBindMapping previousListDataBindMapping) {
            for (ListButtonBindingListener listener : this) {
                listener.listDataBindMappingChanged(listButton, previousListDataBindMapping);
            }
        }

        @Override
        public void selectedItemKeyChanged(ListButton listButton, String previousSelectedItemKey) {
            for (ListButtonBindingListener listener : this) {
                listener.selectedItemKeyChanged(listButton, previousSelectedItemKey);
            }
        }

        @Override
        public void selectedItemBindTypeChanged(ListButton listButton, BindType previousSelectedItemBindType) {
            for (ListButtonBindingListener listener : this) {
                listener.selectedItemBindTypeChanged(listButton, previousSelectedItemBindType);
            }
        }

        @Override
        public void selectedItemBindMappingChanged(ListButton listButton, ListView.ItemBindMapping previousSelectedItemBindMapping) {
            for (ListButtonBindingListener listener : this) {
                listener.selectedItemBindMappingChanged(listButton, previousSelectedItemBindMapping);
            }
        }
    }

    /**
     * ListButton skin interface. ListButton skins must implement
     * this interface to facilitate additional communication between the
     * component and the skin.
     */
    public interface Skin {
        public Window getListViewPopup();
    }

    private List listData;
    private ListView.ItemRenderer itemRenderer;
    private boolean repeatable = false;
    private int selectedIndex = -1;
    private Filter disabledItemFilter = null;
    private int listSize = -1;

    private String listDataKey = null;
    private BindType listDataBindType = BindType.BOTH;
    private ListDataBindMapping listDataBindMapping = null;

    private String selectedItemKey = null;
    private BindType selectedItemBindType = BindType.BOTH;
    private ListView.ItemBindMapping selectedItemBindMapping = null;

    private ListListener listDataListener = new ListListener() {
        @Override
        public void itemInserted(List list, int index) {
            int previousSelectedIndex = selectedIndex;
            if (index <= selectedIndex) {
                selectedIndex++;
            }

            listButtonItemListeners.itemInserted(ListButton.this, index);

            if (selectedIndex != previousSelectedIndex) {
                listButtonSelectionListeners.selectedIndexChanged(ListButton.this, selectedIndex);
            }
        }

        @Override
        public void itemsRemoved(List list, int index, Sequence items) {
            int count = items.getLength();

            int previousSelectedIndex = selectedIndex;

            if (selectedIndex >= index) {
                if (selectedIndex < index + count) {
                    selectedIndex = -1;
                } else {
                    selectedIndex -= count;
                }
            }

            listButtonItemListeners.itemsRemoved(ListButton.this, index, count);

            if (selectedIndex != previousSelectedIndex) {
                listButtonSelectionListeners.selectedIndexChanged(ListButton.this, selectedIndex);

                if (selectedIndex == -1) {
                    listButtonSelectionListeners.selectedItemChanged(ListButton.this, null);
                }
            }
        }

        @Override
        public void itemUpdated(List list, int index, Object previousItem) {
            listButtonItemListeners.itemUpdated(ListButton.this, index);
        }

        @Override
        public void listCleared(List list) {
            int previousSelectedIndex = selectedIndex;
            selectedIndex = -1;

            listButtonItemListeners.itemsCleared(ListButton.this);

            if (previousSelectedIndex != selectedIndex) {
                listButtonSelectionListeners.selectedIndexChanged(ListButton.this, selectedIndex);
                listButtonSelectionListeners.selectedItemChanged(ListButton.this, getSelectedItem());
            }
        }

        @Override
        public void comparatorChanged(List list, Comparator previousComparator) {
            if (list.getComparator() != null) {
                int previousSelectedIndex = selectedIndex;
                selectedIndex = -1;

                listButtonItemListeners.itemsSorted(ListButton.this);

                if (previousSelectedIndex != selectedIndex) {
                    listButtonSelectionListeners.selectedIndexChanged(ListButton.this, selectedIndex);
                    listButtonSelectionListeners.selectedItemChanged(ListButton.this, getSelectedItem());
                }
            }
        }
    };

    private ListButtonListenerList listButtonListeners = new ListButtonListenerList();
    private ListButtonItemListenerList listButtonItemListeners = new ListButtonItemListenerList();
    private ListButtonSelectionListenerList listButtonSelectionListeners = new ListButtonSelectionListenerList();
    private ListButtonBindingListenerList listButtonBindingListeners = new ListButtonBindingListenerList();

    private static final Button.DataRenderer DEFAULT_DATA_RENDERER = new ListButtonDataRenderer();
    private static final ListView.ItemRenderer DEFAULT_ITEM_RENDERER = new ListViewItemRenderer();

    /**
     * Creates an empty list button.
     */
    public ListButton() {
        this(new ArrayList());
    }

    /**
     * Creates a list button with the given button data and an empty list.
     *
     * @param buttonData
     */
    public ListButton(Object buttonData) {
        this(buttonData, new ArrayList());
    }

    /**
     * Creates a list button with no button data and the given list data.
     * @param listData
     */
    public ListButton(List listData) {
        this(null, listData);
    }

    /**
     * Creates a list button with the given button and list data.
     * 

* Note that the default renderer uses (as last option) the toString method on list elements, * so override it to return whatever you want to display in the ListView, * or implement your own custom renderer. * * @param buttonData * The button data. * * @param listData * The data to set. * * @see ListButtonDataRenderer * @see ListViewItemRenderer */ public ListButton(Object buttonData, List listData) { super(buttonData); setDataRenderer(DEFAULT_DATA_RENDERER); setItemRenderer(DEFAULT_ITEM_RENDERER); setListData(listData); installSkin(ListButton.class); } @Override protected void setSkin(org.apache.pivot.wtk.Skin skin) { if (!(skin instanceof ListButton.Skin)) { throw new IllegalArgumentException("Skin class must implement " + ListButton.Skin.class.getName()); } super.setSkin(skin); } /** * @return the popup window associated with this components skin */ public Window getListPopup() { return ((ListButton.Skin) getSkin()).getListViewPopup(); } /** * @throws UnsupportedOperationException * This method is not supported by ListButton. */ @Override public void setToggleButton(boolean toggleButton) { throw new UnsupportedOperationException("List buttons cannot be toggle buttons."); } /** * Returns the list data associated with this list button. * * @return * The list data. */ public List getListData() { return listData; } /** * Sets the list button's list data. * * @param listData * The list data to be presented by the list button. */ @SuppressWarnings("unchecked") public void setListData(List listData) { if (listData == null) { throw new IllegalArgumentException("listData is null."); } List previousListData = this.listData; if (previousListData != listData) { int previousSelectedIndex = selectedIndex; if (previousListData != null) { // Clear any existing selection selectedIndex = -1; ((List)previousListData).getListListeners().remove(listDataListener); } ((List)listData).getListListeners().add(listDataListener); // Update the list data and fire change event this.listData = listData; listButtonListeners.listDataChanged(this, previousListData); if (selectedIndex != previousSelectedIndex) { listButtonSelectionListeners.selectedIndexChanged(this, selectedIndex); listButtonSelectionListeners.selectedItemChanged(this, null); } } } /** * Sets the list button's list data. * * @param listData * The list data to be presented by the list button as a JSON array. */ public final void setListData(String listData) { if (listData == null) { throw new IllegalArgumentException("listData is null."); } try { setListData(JSONSerializer.parseList(listData)); } catch (SerializationException exception) { throw new IllegalArgumentException(exception); } } /** * Sets the list button's list data. * * @param listData * A URL referring to a JSON file containing the data to be presented by * the list button. */ public void setListData(URL listData) { if (listData == null) { throw new IllegalArgumentException("listData is null."); } JSONSerializer jsonSerializer = new JSONSerializer(); try { setListData((List)jsonSerializer.readObject(listData.openStream())); } catch (SerializationException exception) { throw new IllegalArgumentException(exception); } catch (IOException exception) { throw new IllegalArgumentException(exception); } } /** * Returns the renderer used to display items in the list. * * @return * The item renderer instance. */ public ListView.ItemRenderer getItemRenderer() { return itemRenderer; } /** * Sets the renderer used to display items in the list. *

* Use {@link #setDataRenderer(org.apache.pivot.wtk.Button.DataRenderer)} to define * the renderer used to draw the button data. * * @param itemRenderer * The item renderer instance. */ public void setItemRenderer(ListView.ItemRenderer itemRenderer) { ListView.ItemRenderer previousItemRenderer = this.itemRenderer; if (previousItemRenderer != itemRenderer) { this.itemRenderer = itemRenderer; listButtonListeners.itemRendererChanged(this, previousItemRenderer); } } /** * Returns the list button's repeatable flag. */ public boolean isRepeatable() { return repeatable; } /** * Sets the list button's repeatable flag. * * @param repeatable */ public void setRepeatable(boolean repeatable) { if (this.repeatable != repeatable) { this.repeatable = repeatable; listButtonListeners.repeatableChanged(this); } } /** * Returns the current selection. * * @return * The index of the currently selected list item, or -1 if * nothing is selected. */ public int getSelectedIndex() { return selectedIndex; } /** * Sets the selection. * * @param selectedIndex * The index of the list item to select, or -1 to clear the * selection. */ public void setSelectedIndex(int selectedIndex) { indexBoundsCheck("selectedIndex", selectedIndex, -1, listData.getLength() - 1); int previousSelectedIndex = this.selectedIndex; if (previousSelectedIndex != selectedIndex) { this.selectedIndex = selectedIndex; listButtonSelectionListeners.selectedIndexChanged(this, previousSelectedIndex); listButtonSelectionListeners.selectedItemChanged(this, (previousSelectedIndex == -1) ? null : listData.get(previousSelectedIndex)); } } public Object getSelectedItem() { int index = getSelectedIndex(); Object item = null; if (index >= 0) { item = listData.get(index); } return item; } @SuppressWarnings("unchecked") public void setSelectedItem(Object item) { setSelectedIndex((item == null) ? -1 : ((List)listData).indexOf(item)); } /** * Returns an item's disabled state. * * @param index * The index of the item whose disabled state is to be tested. * * @return * true if the item is disabled; false, * otherwise. */ @SuppressWarnings("unchecked") public boolean isItemDisabled(int index) { boolean disabled = false; if (disabledItemFilter != null) { Object item = listData.get(index); disabled = ((Filter)disabledItemFilter).include(item); } return disabled; } /** * Returns the disabled item filter. * * @return * The disabled item filter, or null if no disabled item filter is * set. */ public Filter getDisabledItemFilter() { return disabledItemFilter; } /** * Sets the disabled item filter. * * @param disabledItemFilter * The disabled item filter, or null for no disabled item filter. */ public void setDisabledItemFilter(Filter disabledItemFilter) { Filter previousDisabledItemFilter = this.disabledItemFilter; if (previousDisabledItemFilter != disabledItemFilter) { this.disabledItemFilter = disabledItemFilter; listButtonListeners.disabledItemFilterChanged(this, previousDisabledItemFilter); } } /** * Returns the list size. */ public int getListSize() { return listSize; } /** * Sets the list size. If the number of items in the list exceeds this value, * the list will scroll. * * @param listSize */ public void setListSize(int listSize) { if (listSize < -1) { throw new IllegalArgumentException("Invalid list size."); } int previousListSize = this.listSize; if (previousListSize != listSize) { this.listSize = listSize; listButtonListeners.listSizeChanged(this, previousListSize); } } /** * Returns name of the key that is used in context binding. * * @return * The key. */ public String getListDataKey() { return listDataKey; } /** * Set the name of the key that is used in context binding. * * @param listDataKey * The key to set. */ public void setListDataKey(String listDataKey) { String previousListDataKey = this.listDataKey; if (previousListDataKey != listDataKey) { this.listDataKey = listDataKey; listButtonBindingListeners.listDataKeyChanged(this, previousListDataKey); } } public BindType getListDataBindType() { return listDataBindType; } public void setListDataBindType(BindType listDataBindType) { if (listDataBindType == null) { throw new IllegalArgumentException(); } BindType previousListDataBindType = this.listDataBindType; if (previousListDataBindType != listDataBindType) { this.listDataBindType = listDataBindType; listButtonBindingListeners.listDataBindTypeChanged(this, previousListDataBindType); } } public ListDataBindMapping getListDataBindMapping() { return listDataBindMapping; } public void setListDataBindMapping(ListDataBindMapping listDataBindMapping) { ListDataBindMapping previousListDataBindMapping = this.listDataBindMapping; if (previousListDataBindMapping != listDataBindMapping) { this.listDataBindMapping = listDataBindMapping; listButtonBindingListeners.listDataBindMappingChanged(this, previousListDataBindMapping); } } public String getSelectedItemKey() { return selectedItemKey; } public void setSelectedItemKey(String selectedItemKey) { String previousSelectedItemKey = this.selectedItemKey; if (previousSelectedItemKey != selectedItemKey) { this.selectedItemKey = selectedItemKey; listButtonBindingListeners.selectedItemKeyChanged(this, previousSelectedItemKey); } } public BindType getSelectedItemBindType() { return selectedItemBindType; } public void setSelectedItemBindType(BindType selectedItemBindType) { if (selectedItemBindType == null) { throw new IllegalArgumentException(); } BindType previousSelectedItemBindType = this.selectedItemBindType; if (previousSelectedItemBindType != selectedItemBindType) { this.selectedItemBindType = selectedItemBindType; listButtonBindingListeners.selectedItemBindTypeChanged(this, previousSelectedItemBindType); } } public ListView.ItemBindMapping getSelectedItemBindMapping() { return selectedItemBindMapping; } public void setSelectedItemBindMapping(ListView.ItemBindMapping selectedItemBindMapping) { ListView.ItemBindMapping previousSelectedItemBindMapping = this.selectedItemBindMapping; if (previousSelectedItemBindMapping != selectedItemBindMapping) { this.selectedItemBindMapping = selectedItemBindMapping; listButtonBindingListeners.selectedItemBindMappingChanged(this, previousSelectedItemBindMapping); } } @Override @SuppressWarnings("unchecked") public void load(Object context) { // Bind to list data if (listDataKey != null && listDataBindType != BindType.STORE && JSON.containsKey(context, listDataKey)) { Object value = JSON.get(context, listDataKey); List listDataLocal; if (listDataBindMapping == null) { listDataLocal = (List)value; } else { listDataLocal = listDataBindMapping.toListData(value); } setListData(listDataLocal); } // Bind to selected item if (selectedItemKey != null && selectedItemBindType != BindType.STORE && JSON.containsKey(context, selectedItemKey)) { Object item = JSON.get(context, selectedItemKey); int index; if (selectedItemBindMapping == null) { index = ((List)listData).indexOf(item); } else { index = selectedItemBindMapping.indexOf(listData, item); } setSelectedIndex(index); } } @Override public void store(Object context) { // Bind to list data if (listDataKey != null && listDataBindType != BindType.LOAD) { Object value; if (listDataBindMapping == null) { value = listData; } else { value = listDataBindMapping.valueOf(listData); } JSON.put(context, listDataKey, value); } // Bind to selected item if (selectedItemKey != null && selectedItemBindType != BindType.LOAD) { Object item; int selectedIndexLocal = getSelectedIndex(); if (selectedItemBindMapping == null) { if (selectedIndexLocal == -1) { item = null; } else { item = listData.get(selectedIndexLocal); } } else { item = selectedItemBindMapping.get(listData, selectedIndexLocal); } JSON.put(context, selectedItemKey, item); } } @Override public void clear() { if (listDataKey != null) { setListData(new ArrayList()); } if (selectedItemKey != null) { setSelectedItem(null); } } /** * Clears the selection. */ public void clearSelection() { setSelectedItem(null); } /** * Returns the list button listener list. */ public ListenerList getListButtonListeners() { return listButtonListeners; } /** * Returns the list button item listener list. */ public ListenerList getListButtonItemListeners() { return listButtonItemListeners; } /** * Returns the list button selection listener list. */ public ListenerList getListButtonSelectionListeners() { return listButtonSelectionListeners; } /** * Returns the list button binding listener list. */ public ListenerList getListButtonBindingListeners() { return listButtonBindingListeners; } }