001/*
002 * $Id: HeavyTabbedPane.java,v 1.7 2011/03/24 16:06:34 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
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
031package edu.wisc.ssec.mcidasv.ui;
032
033import java.awt.Component;
034import java.util.ArrayList;
035import java.util.List;
036
037import javax.swing.Icon;
038import javax.swing.JPanel;
039import 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.7 2011/03/24 16:06:34 davep Exp $
054 */
055public 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}