001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2015
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
029package edu.wisc.ssec.mcidasv.ui;
030
031import java.awt.Component;
032import java.util.ArrayList;
033import java.util.List;
034
035import javax.swing.Icon;
036import javax.swing.JPanel;
037import 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 {@code Canvas3D} objects in
047 * a {@code JTabbedPane}, 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 */
053public class HeavyTabbedPane extends JTabbedPane {
054        private static final long serialVersionUID = -3903797547171213551L;
055        
056        /**
057         * Delay in milliseconds for {@link javax.swing.event.ChangeEvent ChangeEvents}.
058     * This prevents some 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         * {@code ChangeEvent} 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}