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 }