blob: 94c4fa321e90472df2e14c1a4f079126af6ec86e [file] [log] [blame]
/*
* 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.annotations.UnsupportedOperation;
import org.apache.pivot.beans.DefaultProperty;
import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.util.ImmutableIterator;
import org.apache.pivot.util.ListenerList;
import org.apache.pivot.util.Utils;
import org.apache.pivot.util.Vote;
import org.apache.pivot.wtk.content.AccordionHeaderDataRenderer;
/**
* Component that provides access to a set of components via selectable headers.
* Only one component is visible at a time.
*/
@DefaultProperty("panels")
public class Accordion extends Container {
/**
* Panel sequence implementation.
*/
public final class PanelSequence implements Sequence<Component>, Iterable<Component> {
private PanelSequence() {
}
@Override
public int add(final Component panel) {
int index = getLength();
insert(panel, index);
return index;
}
@Override
public void insert(final Component panel, final int index) {
Utils.checkNull(panel, "panel");
// Add the panel to the component sequence
Accordion.this.add(panel);
panels.insert(panel, index);
// Update the selection
int previousSelectedIndex = selectedIndex;
if (selectedIndex >= index) {
selectedIndex++;
}
// Fire insert event
accordionListeners.panelInserted(Accordion.this, index);
// Fire selection change event, if necessary
if (selectedIndex != previousSelectedIndex && previousSelectedIndex > -1) {
accordionSelectionListeners.selectedIndexChanged(Accordion.this, selectedIndex);
}
}
@Override
@UnsupportedOperation
public Component update(final int index, final Component panel) {
throw new UnsupportedOperationException();
}
@Override
public int remove(final Component panel) {
int index = indexOf(panel);
if (index != -1) {
remove(index, 1);
}
return index;
}
@Override
public Sequence<Component> remove(final int index, final int count) {
// Remove the panels from the panel list
Sequence<Component> removed = panels.remove(index, count);
// Update the selection
int previousSelectedIndex = selectedIndex;
if (selectedIndex >= index) {
if (selectedIndex < index + count) {
selectedIndex = -1;
} else {
selectedIndex -= count;
}
}
// Fire remove event
accordionListeners.panelsRemoved(Accordion.this, index, removed);
// Remove the panels from the component list
for (int i = 0, n = removed.getLength(); i < n; i++) {
Component panel = removed.get(i);
Accordion.this.remove(panel);
}
// Fire selection change event, if necessary
if (selectedIndex != previousSelectedIndex && previousSelectedIndex > -1) {
accordionSelectionListeners.selectedIndexChanged(Accordion.this, selectedIndex);
}
return removed;
}
@Override
public Component get(final int index) {
return panels.get(index);
}
@Override
public int indexOf(final Component panel) {
return panels.indexOf(panel);
}
@Override
public int getLength() {
return panels.getLength();
}
@Override
public Iterator<Component> iterator() {
return new ImmutableIterator<>(panels.iterator());
}
}
private enum Attribute {
HEADER_DATA,
TOOLTIP_TEXT;
}
private ArrayList<Component> panels = new ArrayList<>();
private PanelSequence panelSequence = new PanelSequence();
private int selectedIndex = -1;
private Button.DataRenderer headerDataRenderer = DEFAULT_HEADER_DATA_RENDERER;
private AccordionListener.Listeners accordionListeners = new AccordionListener.Listeners();
private AccordionSelectionListener.Listeners accordionSelectionListeners =
new AccordionSelectionListener.Listeners();
private AccordionAttributeListener.Listeners accordionAttributeListeners =
new AccordionAttributeListener.Listeners();
private static final Button.DataRenderer DEFAULT_HEADER_DATA_RENDERER = new AccordionHeaderDataRenderer();
public Accordion() {
installSkin(Accordion.class);
}
public PanelSequence getPanels() {
return panelSequence;
}
public int getSelectedIndex() {
return selectedIndex;
}
public void setSelectedIndex(final int selectedIndex) {
indexBoundsCheck("selectedIndex", selectedIndex, -1, panels.getLength() - 1);
int previousSelectedIndex = this.selectedIndex;
if (previousSelectedIndex != selectedIndex) {
Vote vote = accordionSelectionListeners.previewSelectedIndexChange(this, selectedIndex);
if (vote == Vote.APPROVE) {
this.selectedIndex = selectedIndex;
accordionSelectionListeners.selectedIndexChanged(this, previousSelectedIndex);
} else {
accordionSelectionListeners.selectedIndexChangeVetoed(this, vote);
}
}
}
public Component getSelectedPanel() {
return (selectedIndex == -1) ? null : panels.get(selectedIndex);
}
public Button.DataRenderer getHeaderDataRenderer() {
return headerDataRenderer;
}
public void setHeaderDataRenderer(final Button.DataRenderer headerDataRenderer) {
Utils.checkNull(headerDataRenderer, "headerDataRenderer");
Button.DataRenderer previousHeaderDataRenderer = this.headerDataRenderer;
if (previousHeaderDataRenderer != headerDataRenderer) {
this.headerDataRenderer = headerDataRenderer;
accordionListeners.headerDataRendererChanged(this, previousHeaderDataRenderer);
}
}
@Override
public Sequence<Component> remove(final int index, final int count) {
for (int i = index, n = index + count; i < n; i++) {
Component component = get(i);
if (panels.indexOf(component) >= 0) {
throw new UnsupportedOperationException("Cannot remove a panel directly from the Accordion.");
}
}
// Call the base method to remove the components
return super.remove(index, count);
}
public ListenerList<AccordionListener> getAccordionListeners() {
return accordionListeners;
}
public ListenerList<AccordionSelectionListener> getAccordionSelectionListeners() {
return accordionSelectionListeners;
}
public ListenerList<AccordionAttributeListener> getAccordionAttributeListeners() {
return accordionAttributeListeners;
}
public static Object getHeaderData(final Component component) {
return component.getAttribute(Attribute.HEADER_DATA);
}
public static void setHeaderData(final Component component, final Object headerData) {
Object previousHeaderData = component.setAttribute(Attribute.HEADER_DATA, headerData);
if (previousHeaderData != headerData) {
Container parent = component.getParent();
if (parent instanceof Accordion) {
Accordion accordion = (Accordion) parent;
accordion.accordionAttributeListeners.headerDataChanged(accordion, component,
previousHeaderData);
}
}
}
public static String getTooltipText(final Component component) {
return (String) component.getAttribute(Attribute.TOOLTIP_TEXT);
}
public static void setTooltipText(final Component component, final String tooltipText) {
String previousTooltipText = (String) component.setAttribute(Attribute.TOOLTIP_TEXT,
tooltipText);
if (previousTooltipText != tooltipText) {
Container parent = component.getParent();
if (parent instanceof Accordion) {
Accordion accordion = (Accordion) parent;
accordion.accordionAttributeListeners.tooltipTextChanged(accordion, component,
previousTooltipText);
}
}
}
}