JCardPane: Unterschied zwischen den Versionen

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen
K
 
(5 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
<code=java>
+
Die im Bild dargestellte GUI verwendet eine {{JAPI|JTabbedPane}}, deren Reiter versteckt sind. Der Wechsel auf eine andere Komponente in der JTabbedPane, also einen anderen Reiter, erfolgt mit Hilfe der [[JButton|JButtons]].
 +
 
 +
Bei dem Beispiel gibt es also große Ähnlichkeit mit einem [[CardLayout]].
 +
 
 +
[[Bild:JCardPaneDemo.jpg]]
 +
<syntaxhighlight lang="java">
 
/*
 
/*
 
  * JCardPane.java
 
  * JCardPane.java
Zeile 5: Zeile 10:
 
  * The main method shows an example.
 
  * The main method shows an example.
 
  */
 
  */
 
 
import java.awt.*;
 
import java.awt.*;
 
import java.awt.event.*;
 
import java.awt.event.*;
 
import java.beans.*;
 
import java.beans.*;
 
import java.util.*;
 
import java.util.*;
 +
import java.util.logging.*;
 
import javax.swing.*;
 
import javax.swing.*;
 
import javax.swing.event.*;
 
import javax.swing.event.*;
import javax.swing.plaf.TabbedPaneUI;
+
import javax.swing.plaf.*;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
+
import javax.swing.plaf.basic.*;
  
 
public class JCardPane extends JComponent {
 
public class JCardPane extends JComponent {
Zeile 58: Zeile 63:
 
         pane.addChangeListener(new ChangeListener() {
 
         pane.addChangeListener(new ChangeListener() {
  
             public void stateChanged(ChangeEvent e) {
+
            @Override
 +
             public void stateChanged(final ChangeEvent e) {
 
                 if (nextAction != null) {
 
                 if (nextAction != null) {
 
                     nextAction.setEnabled(hasNext());
 
                     nextAction.setEnabled(hasNext());
Zeile 68: Zeile 74:
 
         });
 
         });
 
         setLayout(new BorderLayout());
 
         setLayout(new BorderLayout());
         add(pane);
+
         super.add(pane);
 
     }
 
     }
  
Zeile 165: Zeile 171:
 
         pane.setSelectedIndex(index);
 
         pane.setSelectedIndex(index);
 
         return index;
 
         return index;
 +
    }
 +
 +
    /**
 +
    * Flips to the specified card.
 +
    * If the card does not exist in the cardpane, it is added to it.
 +
    * @return the index of the specified card
 +
    */
 +
    public int add(final JComponent card) {
 +
        return show(card);
 +
    }
 +
 +
    /**
 +
    * Inserts the specified card at the sepcified index and
 +
    * flips to the specified card.
 +
    */
 +
    public void insertAt(JComponent card, int index){
 +
        pane.insertTab(null, null, card, null, index);
 +
        pane.setSelectedIndex(index);
 
     }
 
     }
  
Zeile 191: Zeile 215:
 
     * Adds a <code>ChangeListener</code> to this cardpane.
 
     * Adds a <code>ChangeListener</code> to this cardpane.
 
     */
 
     */
     public void addChangeListener(ChangeListener l) {
+
     public void addChangeListener(final ChangeListener l) {
 
         pane.addChangeListener(l);
 
         pane.addChangeListener(l);
 
     }
 
     }
Zeile 198: Zeile 222:
 
     * Removes a <code>ChangeListener</code> from this cardpane.
 
     * Removes a <code>ChangeListener</code> from this cardpane.
 
     */
 
     */
     public void removeChangeListener(ChangeListener l) {
+
     public void removeChangeListener(final ChangeListener l) {
 
         pane.removeChangeListener(l);
 
         pane.removeChangeListener(l);
 
     }
 
     }
Zeile 205: Zeile 229:
 
     * gets the action to be used for a "Next" button
 
     * gets the action to be used for a "Next" button
 
     */
 
     */
     public Action getNextAction() {
+
     public Action getNextAction(final String name) {
 
         if (nextAction == null) {
 
         if (nextAction == null) {
             nextAction = new CardAction("Next", true);
+
             nextAction = new CardAction(name, true);
             nextAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_N);
+
             nextAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0));
 
             nextAction.setEnabled(hasNext());
 
             nextAction.setEnabled(hasNext());
 
         }
 
         }
Zeile 217: Zeile 241:
 
     * gets the action to be used for a "Previous" button
 
     * gets the action to be used for a "Previous" button
 
     */
 
     */
     public Action getPreviousAction() {
+
     public Action getPreviousAction(final String name) {
 
         if (previousAction == null) {
 
         if (previousAction == null) {
             previousAction = new CardAction("Previous", false);
+
             previousAction = new CardAction(name, false);
             previousAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_P);
+
             previousAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0));
 
             previousAction.setEnabled(hasPrevious());
 
             previousAction.setEnabled(hasPrevious());
 
         }
 
         }
Zeile 227: Zeile 251:
  
 
     /**
 
     /**
     *  
+
     *
 
     * retain: if true, the component focused within a card will remain focused
 
     * retain: if true, the component focused within a card will remain focused
 
     * each time it becomes visible, otherwise the first component of each card is focused
 
     * each time it becomes visible, otherwise the first component of each card is focused
 
     */
 
     */
     public void retainFocus(boolean retain) {
+
     public void retainFocus(final boolean retain) {
 
         int focusPolicy = TabFocusHandler.RESET_FOCUS;
 
         int focusPolicy = TabFocusHandler.RESET_FOCUS;
 
         if (retain) {
 
         if (retain) {
Zeile 248: Zeile 272:
 
         private boolean isNext;
 
         private boolean isNext;
  
         public CardAction(String text, boolean isNext) {
+
         CardAction(final String text, final boolean isNext) {
 
             super(text);
 
             super(text);
 
             this.isNext = isNext;
 
             this.isNext = isNext;
Zeile 254: Zeile 278:
 
         }
 
         }
  
         public void actionPerformed(ActionEvent e) {
+
        @Override
 +
         public void actionPerformed(final ActionEvent e) {
 
             if (isNext) {
 
             if (isNext) {
 
                 next();
 
                 next();
Zeile 271: Zeile 296:
 
     *  in which case the other policy will be in effect.
 
     *  in which case the other policy will be in effect.
 
     */
 
     */
     private class TabFocusHandler implements ChangeListener, PropertyChangeListener {
+
     private class TabFocusHandler {
  
 
         public final static int RESET_FOCUS = 0;
 
         public final static int RESET_FOCUS = 0;
 
         public final static int RETAIN_FOCUS = 1;
 
         public final static int RETAIN_FOCUS = 1;
         private HashMap<Component, Component> tabFocus = new HashMap<Component, Component>();
+
         private HashMap<Component, Component> tabFocus = new HashMap<Component, Component>(16);
 
         private HashSet<Component> exceptions;
 
         private HashSet<Component> exceptions;
 
         private JTabbedPane tabbedPane;
 
         private JTabbedPane tabbedPane;
 
         private int focusPolicy;
 
         private int focusPolicy;
 
         private final KeyboardFocusManager focusManager;
 
         private final KeyboardFocusManager focusManager;
 +
        private final ChangeListener changeListener;
 +
        private final PropertyChangeListener propertyChangeListener;
  
 
         /**
 
         /**
 
         *  Create with the default Retain Focus policy
 
         *  Create with the default Retain Focus policy
 
         */
 
         */
         public TabFocusHandler(JTabbedPane tabbedPane) {
+
         TabFocusHandler(final JTabbedPane tabbedPane) {
 
             this(tabbedPane, RETAIN_FOCUS);
 
             this(tabbedPane, RETAIN_FOCUS);
 
         }
 
         }
Zeile 291: Zeile 318:
 
         *  Create using the specified focus policy
 
         *  Create using the specified focus policy
 
         */
 
         */
         public TabFocusHandler(JTabbedPane tabbedPane, int focusPolicy) {
+
         TabFocusHandler(final JTabbedPane tabbedPane, final int focusPolicy) {
 
             if (focusPolicy != RESET_FOCUS && focusPolicy != RETAIN_FOCUS) {
 
             if (focusPolicy != RESET_FOCUS && focusPolicy != RETAIN_FOCUS) {
 
                 throw new IllegalArgumentException("Invalid focus policy");
 
                 throw new IllegalArgumentException("Invalid focus policy");
Zeile 298: Zeile 325:
 
             this.focusPolicy = focusPolicy;
 
             this.focusPolicy = focusPolicy;
 
             //  Add listeners to manage a tab change
 
             //  Add listeners to manage a tab change
             tabbedPane.addChangeListener(this);
+
            changeListener = new TabChangeListener();
 +
            propertyChangeListener = new TabPropertyChangeListener();
 +
             tabbedPane.addChangeListener(changeListener);
 
             focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
 
             focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
             focusManager.addPropertyChangeListener("permanentFocusOwner", this);
+
             focusManager.addPropertyChangeListener("permanentFocusOwner", propertyChangeListener);
 
         }
 
         }
  
 
         public void uninstall() {
 
         public void uninstall() {
             tabbedPane.removeChangeListener(this);
+
             tabbedPane.removeChangeListener(changeListener);
             focusManager.removePropertyChangeListener(this);
+
             focusManager.removePropertyChangeListener(propertyChangeListener);
 
         }
 
         }
  
Zeile 311: Zeile 340:
 
         *  Specify a tab with an exception to the focus policy rule
 
         *  Specify a tab with an exception to the focus policy rule
 
         */
 
         */
         public void addException(int index) {
+
         public void addException(final int index) {
 
             if (exceptions == null) {
 
             if (exceptions == null) {
                 exceptions = new HashSet<Component>();
+
                 exceptions = new HashSet<Component>(16);
 
             }
 
             }
 
             Component key = tabbedPane.getComponentAt(index);
 
             Component key = tabbedPane.getComponentAt(index);
Zeile 319: Zeile 348:
 
         }
 
         }
  
         /**
+
         private class TabPropertyChangeListener implements PropertyChangeListener {
        * Tab has changed. Focus on saved component for the given tab.
+
 
        * When there is no saved component, focus on the first component.
+
             TabPropertyChangeListener() {
        */
 
        public void stateChanged(ChangeEvent e) {
 
            Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
 
             if (key == null) {
 
                return;
 
 
             }
 
             }
             Component value = tabFocus.get(key);
+
 
            //  First time selecting this tab or focus policy is RESET_FOCUS
+
             /**
            if (value == null) {
+
            *  Track focus changes and update the current focus component
                 key.transferFocus();
+
            *  for the current tab
                 tabFocus.put(key, value);
+
            */
            } else //  Use the saved component for focusing
+
            @Override
            {
+
            public void propertyChange(final PropertyChangeEvent e) {
                 value.requestFocusInWindow();
+
                //  No need to track focus change
 +
                if (exceptions == null && focusPolicy == RESET_FOCUS) {
 +
                    return;
 +
                }
 +
                 Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
 +
                 if (exceptions != null) {
 +
                    if (focusPolicy == RESET_FOCUS && !exceptions.contains(key)) {
 +
                        return;
 +
                    }
 +
                    if (focusPolicy == RETAIN_FOCUS && exceptions.contains(key)) {
 +
                        return;
 +
                    }
 +
                }
 +
                 Component value = (Component) e.getNewValue();
 +
                if (value != null && SwingUtilities.isDescendingFrom(value, key)) {
 +
                    tabFocus.put(key, value);
 +
                }
 
             }
 
             }
 
         }
 
         }
  
         /**
+
         private class TabChangeListener implements ChangeListener {
        *  Track focus changes and update the current focus component
+
 
        *  for the current tab
+
             TabChangeListener() {
        */
 
        public void propertyChange(PropertyChangeEvent e) {
 
            //  No need to track focus change
 
             if (exceptions == null && focusPolicy == RESET_FOCUS) {
 
                return;
 
 
             }
 
             }
             //  Check for exceptions to the focus policy exist
+
 
             Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
+
             /**
            if (exceptions != null) {
+
            * Tab has changed. Focus on saved component for the given tab.
                 if (focusPolicy == RESET_FOCUS && !exceptions.contains(key)) {
+
            * When there is no saved component, focus on the first component.
 +
            */
 +
            @Override
 +
             public void stateChanged(final ChangeEvent e) {
 +
                Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
 +
                 if (key == null) {
 
                     return;
 
                     return;
 
                 }
 
                 }
                 if (focusPolicy == RETAIN_FOCUS && exceptions.contains(key)) {
+
                Component value = tabFocus.get(key);
                     return;
+
                //  First time selecting this tab or focus policy is RESET_FOCUS
 +
                 if (value == null) {
 +
                    key.transferFocus();
 +
                    tabFocus.put(key, value);
 +
                } else //  Use the saved component for focusing
 +
                {
 +
                     value.requestFocusInWindow();
 
                 }
 
                 }
            }
 
            // Track focus changes for the tab
 
            Component value = (Component) e.getNewValue();
 
            if (value != null && SwingUtilities.isDescendingFrom(value, key)) {
 
                tabFocus.put(key, value);
 
 
             }
 
             }
 
         }
 
         }
Zeile 367: Zeile 408:
  
 
     //for testing only:
 
     //for testing only:
     public static void main(final String[] args) {
+
     public static void main(final String... args) {
 
         try {
 
         try {
 
             UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
 
             UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
         } catch (Exception ex) {
+
         } catch (ClassNotFoundException ex) {
             ex.printStackTrace();
+
             Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
 +
        } catch (InstantiationException ex) {
 +
            Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
 +
        } catch (IllegalAccessException ex) {
 +
            Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
 +
        } catch (UnsupportedLookAndFeelException ex) {
 +
            Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
 
         }
 
         }
 
         Runnable gui = new Runnable() {
 
         Runnable gui = new Runnable() {
Zeile 385: Zeile 432:
 
                 class Card extends JPanel {
 
                 class Card extends JPanel {
  
                     public Card(final String name) {
+
                     Card(final String name) {
 
                         JTextField tf = new JTextField(20);
 
                         JTextField tf = new JTextField(20);
 
                         tf.setText(name);
 
                         tf.setText(name);
Zeile 400: Zeile 447:
 
                 cardPane.first();
 
                 cardPane.first();
 
                 JPanel control = new JPanel();
 
                 JPanel control = new JPanel();
                 control.add(new JButton(cardPane.getPreviousAction()));
+
                 control.add(new JButton(cardPane.getPreviousAction("Previous")));
                 control.add(new JButton(cardPane.getNextAction()));
+
                 control.add(new JButton(cardPane.getNextAction("Next")));
 
                 f.add(control, BorderLayout.SOUTH);
 
                 f.add(control, BorderLayout.SOUTH);
 
                 f.setVisible(true);
 
                 f.setVisible(true);
Zeile 410: Zeile 457:
 
     }
 
     }
 
}
 
}
</code=java>
+
</syntaxhighlight>
[[Kategorie:Java]]
+
 
 
[[Kategorie:Swing]]
 
[[Kategorie:Swing]]
 
[[Kategorie:Java-Codeschnipsel]]
 
[[Kategorie:Java-Codeschnipsel]]

Aktuelle Version vom 8. April 2018, 11:45 Uhr

Die im Bild dargestellte GUI verwendet eine JTabbedPane, deren Reiter versteckt sind. Der Wechsel auf eine andere Komponente in der JTabbedPane, also einen anderen Reiter, erfolgt mit Hilfe der JButtons.

Bei dem Beispiel gibt es also große Ähnlichkeit mit einem CardLayout.

JCardPaneDemo.jpg

/*
 * JCardPane.java
 * wraps a JTabbedPane, hiding the tabs.
 * The main method shows an example.
 */
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.util.*;
import java.util.logging.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;

public class JCardPane extends JComponent {

    private JTabbedPane pane;
    private CardAction nextAction;
    private CardAction previousAction;
    private TabFocusHandler tabFocusHandler;
    private TabbedPaneUI uiNoTabs = new BasicTabbedPaneUI() {

        @Override
        protected int calculateTabAreaHeight(int tabPlacement,
                int horizRunCount, int maxTabHeight) {
            return 0;
        }

        @Override
        protected Insets getContentBorderInsets(int tabPlacement) {
            return new Insets(0, 0, 0, 0);
        }

        @Override
        protected MouseListener createMouseListener() {
            return null;
        }

        @Override
        protected void installKeyboardActions() {
        }

        @Override
        protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
            return 0;
        }
    };

    /**
     * Creates an empty cardpane
     */
    public JCardPane() {
        pane = new JTabbedPane();
        tabFocusHandler = new TabFocusHandler(pane);
        hideTabs();
        pane.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(final ChangeEvent e) {
                if (nextAction != null) {
                    nextAction.setEnabled(hasNext());
                }
                if (previousAction != null) {
                    previousAction.setEnabled(hasPrevious());
                }
            }
        });
        setLayout(new BorderLayout());
        super.add(pane);
    }

    /**
     * Flips to the first card of the cardpane.
     * @return 0 if this cardpane has a card
     * or -1 if this cardpane has no card
     */
    public int first() {
        return show(0);
    }

    /**
     * Flips to the last card of the cardpane.
     * @return 0 if this cardpane has a card
     * or -1 if this cardpane has no card
     */
    public int last() {
        return show(-1);
    }

    /**
     * Flips to the next card of the cardpane.
     * If the currently visible card is the last one,
     * this method flips to the first card of the cardpane.
     * @return the index of the card
     * or -1 if this cardpane has no card
     */
    public int next() {
        return show(pane.getSelectedIndex() + 1);
    }

    /**
     * Flips to the previous card of the cardpane.
     * If the currently visible card is the first one,
     * this method flips to the last card of the cardpane.
     * @return the index of the card
     * or -1 if this cardpane has no card
     */
    public int previous() {
        return show(pane.getSelectedIndex() - 1);
    }

    /**
     * Flips to the card at the specified index.
     * If the index is positive but does not exist,
     * this method flips to the first card of the cardpane.
     * If the index is negative,
     * this method flips to the last card of the cardpane.
     * @return the index of the card
     * or -1 if this cardpane has no card
     */
    public int show(final int index) {
        if (pane.getTabCount() == 0) {
            return -1;
        }
        if (index >= pane.getTabCount()) {
            pane.setSelectedIndex(0);
            return 0;

        }
        if (index < 0) {
            pane.setSelectedIndex(pane.getTabCount() - 1);
            return pane.getTabCount() - 1;

        }
        pane.setSelectedIndex(index);
        return index;
    }

    /**
     * Flips to the card with the specified name.
     * If the name does not exist, nothing happens.
     * @return the index of the card
     * or -1 if the name does not exist
     */
    public int show(final String name) {
        int index = pane.indexOfTab(name);
        if (index >= 0) {
            pane.setSelectedIndex(index);
        }
        return index;
    }

    /**
     * Flips to the specified card.
     * If the card does not exist in the cardpane, it is added to it.
     * @return the index of the specified card
     */
    public int show(final JComponent card) {
        int index = pane.indexOfComponent(card);
        if (index < 0) {
            index = pane.getTabCount();
            pane.addTab(card.getName(), card);
        }
        pane.setSelectedIndex(index);
        return index;
    }

    /**
     * Flips to the specified card.
     * If the card does not exist in the cardpane, it is added to it.
     * @return the index of the specified card
     */
    public int add(final JComponent card) {
        return show(card);
    }

    /**
     * Inserts the specified card at the sepcified index and
     * flips to the specified card.
     */
    public void insertAt(JComponent card, int index){
        pane.insertTab(null, null, card, null, index);
        pane.setSelectedIndex(index);
    }

    /**
     * @return true if there is a card after the visible one
     */
    public boolean hasNext() {
        return pane.getSelectedIndex() < pane.getComponentCount() - 1;
    }

    /**
     * @return true if there is a card before the visible one
     */
    public boolean hasPrevious() {
        return pane.getSelectedIndex() > 0;
    }

    /**
     * @return the visible card
     */
    public Component getCurrent() {
        return pane.getSelectedComponent();
    }

    /**
     * Adds a <code>ChangeListener</code> to this cardpane.
     */
    public void addChangeListener(final ChangeListener l) {
        pane.addChangeListener(l);
    }

    /**
     * Removes a <code>ChangeListener</code> from this cardpane.
     */
    public void removeChangeListener(final ChangeListener l) {
        pane.removeChangeListener(l);
    }

    /**
     * gets the action to be used for a "Next" button
     */
    public Action getNextAction(final String name) {
        if (nextAction == null) {
            nextAction = new CardAction(name, true);
            nextAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0));
            nextAction.setEnabled(hasNext());
        }
        return nextAction;
    }

    /**
     * gets the action to be used for a "Previous" button
     */
    public Action getPreviousAction(final String name) {
        if (previousAction == null) {
            previousAction = new CardAction(name, false);
            previousAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0));
            previousAction.setEnabled(hasPrevious());
        }
        return previousAction;
    }

    /**
     *
     * retain: if true, the component focused within a card will remain focused
     * each time it becomes visible, otherwise the first component of each card is focused
     */
    public void retainFocus(final boolean retain) {
        int focusPolicy = TabFocusHandler.RESET_FOCUS;
        if (retain) {
            focusPolicy = TabFocusHandler.RETAIN_FOCUS;
        }
        tabFocusHandler.uninstall();
        tabFocusHandler = new TabFocusHandler(pane, focusPolicy);
    }

    private void hideTabs() {
        pane.setUI(uiNoTabs);
    }

    private class CardAction extends AbstractAction {

        private boolean isNext;

        CardAction(final String text, final boolean isNext) {
            super(text);
            this.isNext = isNext;
            putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
        }

        @Override
        public void actionPerformed(final ActionEvent e) {
            if (isNext) {
                next();
            } else {
                previous();
            }
        }
    }

    /**
     *  Manage the focus when a new tab is selected. You can select a focus policy:
     *  a) Reset Focus - focus is reset to the first focusable component on the tab
     *  b) Retain Focus - focus returns to the last component with focus on the tab
     *
     *  In addition you add tabs that you want to exclude from the focus policy,
     *  in which case the other policy will be in effect.
     */
    private class TabFocusHandler {

        public final static int RESET_FOCUS = 0;
        public final static int RETAIN_FOCUS = 1;
        private HashMap<Component, Component> tabFocus = new HashMap<Component, Component>(16);
        private HashSet<Component> exceptions;
        private JTabbedPane tabbedPane;
        private int focusPolicy;
        private final KeyboardFocusManager focusManager;
        private final ChangeListener changeListener;
        private final PropertyChangeListener propertyChangeListener;

        /**
         *  Create with the default Retain Focus policy
         */
        TabFocusHandler(final JTabbedPane tabbedPane) {
            this(tabbedPane, RETAIN_FOCUS);
        }

        /**
         *  Create using the specified focus policy
         */
        TabFocusHandler(final JTabbedPane tabbedPane, final int focusPolicy) {
            if (focusPolicy != RESET_FOCUS && focusPolicy != RETAIN_FOCUS) {
                throw new IllegalArgumentException("Invalid focus policy");
            }
            this.tabbedPane = tabbedPane;
            this.focusPolicy = focusPolicy;
            //  Add listeners to manage a tab change
            changeListener = new TabChangeListener();
            propertyChangeListener = new TabPropertyChangeListener();
            tabbedPane.addChangeListener(changeListener);
            focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
            focusManager.addPropertyChangeListener("permanentFocusOwner", propertyChangeListener);
        }

        public void uninstall() {
            tabbedPane.removeChangeListener(changeListener);
            focusManager.removePropertyChangeListener(propertyChangeListener);
        }

        /**
         *  Specify a tab with an exception to the focus policy rule
         */
        public void addException(final int index) {
            if (exceptions == null) {
                exceptions = new HashSet<Component>(16);
            }
            Component key = tabbedPane.getComponentAt(index);
            exceptions.add(key);
        }

        private class TabPropertyChangeListener implements PropertyChangeListener {

            TabPropertyChangeListener() {
            }

            /**
             *  Track focus changes and update the current focus component
             *  for the current tab
             */
            @Override
            public void propertyChange(final PropertyChangeEvent e) {
                //  No need to track focus change
                if (exceptions == null && focusPolicy == RESET_FOCUS) {
                    return;
                }
                Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
                if (exceptions != null) {
                    if (focusPolicy == RESET_FOCUS && !exceptions.contains(key)) {
                        return;
                    }
                    if (focusPolicy == RETAIN_FOCUS && exceptions.contains(key)) {
                        return;
                    }
                }
                Component value = (Component) e.getNewValue();
                if (value != null && SwingUtilities.isDescendingFrom(value, key)) {
                    tabFocus.put(key, value);
                }
            }
        }

        private class TabChangeListener implements ChangeListener {

            TabChangeListener() {
            }

            /**
             * Tab has changed. Focus on saved component for the given tab.
             * When there is no saved component, focus on the first component.
             */
            @Override
            public void stateChanged(final ChangeEvent e) {
                Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
                if (key == null) {
                    return;
                }
                Component value = tabFocus.get(key);
                //  First time selecting this tab or focus policy is RESET_FOCUS
                if (value == null) {
                    key.transferFocus();
                    tabFocus.put(key, value);
                } else //  Use the saved component for focusing
                {
                    value.requestFocusInWindow();
                }
            }
        }
    }

    //for testing only:
    public static void main(final String... args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnsupportedLookAndFeelException ex) {
            Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
        }
        Runnable gui = new Runnable() {

            @Override
            public void run() {
                final JFrame f = new JFrame("JCardPane Demo");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setSize(400, 150);
                f.setLocationRelativeTo(null);
                final JCardPane cardPane = new JCardPane();
                f.add(cardPane, BorderLayout.CENTER);
                class Card extends JPanel {

                    Card(final String name) {
                        JTextField tf = new JTextField(20);
                        tf.setText(name);
                        add(tf);
                        tf = new JTextField(20);
                        tf.setText(name);
                        add(tf);
                        setName(name);
                    }
                }
                for (int i = 0; i < 5; i++) {
                    cardPane.show(new Card(String.valueOf(i + 1)));
                }
                cardPane.first();
                JPanel control = new JPanel();
                control.add(new JButton(cardPane.getPreviousAction("Previous")));
                control.add(new JButton(cardPane.getNextAction("Next")));
                f.add(control, BorderLayout.SOUTH);
                f.setVisible(true);
            }
        };
        //GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}