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