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 }