001    /*
002     * $Id: HeavyTabbedPane.java,v 1.8 2012/02/19 17:35:50 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    
031    package edu.wisc.ssec.mcidasv.ui;
032    
033    import java.awt.Component;
034    import java.util.ArrayList;
035    import java.util.List;
036    
037    import javax.swing.Icon;
038    import javax.swing.JPanel;
039    import javax.swing.JTabbedPane;
040    
041    /**
042     * A {@link javax.swing.JTabbedPane} implementation that allows tabbed heavy-weight 
043     * components. When a component is added to a tab it is cached and an associated 
044     * light-weight stand-in component and added instead. When a tab is selected the 
045     * light-weight stand in is removed and it's heavy-weight counter-part is displayed.  
046     * When another tab is selected the reverse happens.
047     * <p>
048     * This was originally written to facilitate the use of <tt>Canvas3D</tt> objects in 
049     * a <tt>JTabbedPane</tt>, but I believe it will work for any heavy-weight component.
050     * <p>
051     * 
052     * @author <a href="https://www.ssec.wisc.edu/cgi-bin/email_form.cgi?name=Flynn,%20Bruce">Bruce Flynn, SSEC</a>
053     * @version $Id: HeavyTabbedPane.java,v 1.8 2012/02/19 17:35:50 davep Exp $
054     */
055    public class HeavyTabbedPane extends JTabbedPane {
056            private static final long serialVersionUID = -3903797547171213551L;
057            
058            /**
059             * Delay in milliseconds for <tt>ChangeEvent</tt>s. This prevents some
060             * re-draw issues that popup with the heavy weight components.
061             */
062            protected long heavyWeightDelay = 0;
063            
064            /**
065             * Components, in tab index order, that will be displayed when a
066             * tab is selected.
067             */
068            private List<Component> comps = new ArrayList<Component>();
069            /**
070             * Components, in tab index order, that will be displayed when a
071             * tab is not selected. These should never actually be visible to the
072             * user.
073             */
074            private List<Component> blanks = new ArrayList<Component>();
075            
076            /**
077             * Create and return the component to be used when a tab is not visible.
078             * @return Component used for tabs that are not currently selected.
079             */
080            protected Component blank() {
081                    return new JPanel();
082            }
083            
084            /**
085             * Set the delay to wait before firing a state change event.
086             * @param d If >= 0, no delay will be used.
087             */
088            protected void setHeavyWeightDeleay(long d) {
089                    if (d < 0) d = 0;
090                    heavyWeightDelay = d;
091            }
092            
093            @Override
094            public void insertTab(String title, Icon ico, Component comp, String tip, int idx) {
095                    Component blank = blank();
096                    blanks.add(idx, blank);
097                    comps.add(idx, comp);
098                    super.insertTab(title, ico, blank, tip, idx);
099            }
100    
101            @Override
102            public int indexOfComponent(Component comp) {
103                    // if the tab count does not equal the size of the component caches
104                    // this was probably called by something internal. This ensures we
105                    // don't return an errant value.
106                    if (getTabCount() == blanks.size() && getTabCount() == comps.size()) {
107                            if (comps.contains(comp)) {
108                                    return comps.indexOf(comp);
109                            } else if (blanks.contains(comp)) {
110                                    return blanks.indexOf(comp);
111                            }
112                    }
113                    return -1;
114            }
115            
116            @Override
117            public Component getComponentAt(int idx) {
118                    // return the actual component, not the blank
119                    return comps.get(idx);
120            }
121            
122            @Override
123            public void setComponentAt(int idx, Component comp) {
124                    // no need to change the blanks
125                    comps.set(idx, comp);
126                    super.setComponentAt(idx, comp);
127            }
128            
129            @Override
130            public void setSelectedIndex(int idx) {
131                    int prevIdx = getSelectedIndex();
132                    super.setSelectedIndex(idx);
133                    // show the actual component for the selected index and change
134                    // the other to it's blank
135                    if (prevIdx != -1 && idx != -1) {
136                            super.setComponentAt(prevIdx, blanks.get(prevIdx));
137                            super.setComponentAt(idx, comps.get(idx));
138                    }
139            }
140            
141            @Override
142            public void setSelectedComponent(Component comp) {
143                    if (comp == null || comps.indexOf(comp) < 0) {
144                            throw new IllegalArgumentException("Component not found in tabbed pane");
145                    }
146                    int idx = comps.indexOf(comp);
147                    setSelectedIndex(idx);
148            }
149            
150            @Override
151            public void removeTabAt(int idx) {
152                    super.removeTabAt(idx);
153                    comps.remove(idx);
154                    blanks.remove(idx);
155            }
156            
157            @Override
158            public void remove(int idx) {
159                    removeTabAt(idx);
160            }
161            
162            @Override
163            public void removeAll() {
164                    super.removeAll();
165                    comps.clear();
166                    blanks.clear();
167            }
168            
169            /**
170             * <tt>ChangeEvent</tt> are delayed by the heavy weight delay 
171             * milliseconds to aid in the proper rendering of heavy weight components.
172             * @see javax.swing.JTabbedPane#fireStateChanged()
173             */
174            @Override
175            protected void fireStateChanged() {
176                    try {
177                            Thread.sleep(heavyWeightDelay);
178                    } catch (InterruptedException e) {}
179                    super.fireStateChanged();
180            }
181            
182    //      public static void main(String[] args) throws Exception {
183    //              javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getCrossPlatformLookAndFeelClassName());
184    //              JFrame frame = new JFrame("J3DTabbedPane");
185    //              frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
186    //              final JTabbedPane tabs = new HeavyTabbedPane();
187    //              frame.setLayout(new BorderLayout());
188    //              frame.add(tabs, BorderLayout.CENTER);
189    //              JPanel panel = new JPanel();
190    //              panel.setLayout(new BorderLayout());
191    //              panel.setName("BluePanel");
192    //              panel.add(new JLabel("Actual"), BorderLayout.BEFORE_FIRST_LINE);
193    //              panel.setBackground(Color.BLUE);
194    //              DisplayImpl display = new DisplayImplJ3D("Blue");
195    //              panel.add(display.getComponent(), BorderLayout.CENTER);
196    //              tabs.add("BluePanel", panel);
197    //              panel = new JPanel();
198    //              panel.setLayout(new BorderLayout());
199    //              display = new DisplayImplJ3D("Red");
200    //              panel.add(display.getComponent(), BorderLayout.CENTER);
201    //              panel.setName("RedPanel");
202    //              panel.add(new JLabel("Actual"), BorderLayout.BEFORE_FIRST_LINE);
203    //              panel.setBackground(Color.RED);
204    //              tabs.add("RedPanel", panel);
205    //              frame.setSize(400, 600);
206    //              frame.setVisible(true);
207    //              
208    //              tabs.addChangeListener(new ChangeListener() {
209    //                      public void stateChanged(ChangeEvent e) {
210    //                              System.err.println();
211    //                              for (int i=0; i<tabs.getTabCount(); i++) {
212    //                                      System.err.println("Tab " + i + " " + tabs.getComponentAt(i).getName());
213    //                              }
214    //                      }
215    //              });
216    //      }
217    }