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 }