001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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<>(); 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<>(); 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 * 085 * @param d If >= 0, no delay will be used. 086 */ 087 protected void setHeavyWeightDeleay(long d) { 088 if (d < 0) d = 0; 089 heavyWeightDelay = d; 090 } 091 092 @Override 093 public void insertTab(String title, Icon ico, Component comp, String tip, int idx) { 094 Component blank = blank(); 095 blanks.add(idx, blank); 096 comps.add(idx, comp); 097 super.insertTab(title, ico, blank, tip, idx); 098 } 099 100 @Override 101 public int indexOfComponent(Component comp) { 102 // if the tab count does not equal the size of the component caches 103 // this was probably called by something internal. This ensures we 104 // don't return an errant value. 105 if (getTabCount() == blanks.size() && getTabCount() == comps.size()) { 106 if (comps.contains(comp)) { 107 return comps.indexOf(comp); 108 } else if (blanks.contains(comp)) { 109 return blanks.indexOf(comp); 110 } 111 } 112 return -1; 113 } 114 115 @Override 116 public Component getComponentAt(int idx) { 117 // return the actual component, not the blank 118 return comps.get(idx); 119 } 120 121 @Override 122 public void setComponentAt(int idx, Component comp) { 123 // no need to change the blanks 124 comps.set(idx, comp); 125 super.setComponentAt(idx, comp); 126 } 127 128 @Override 129 public void setSelectedIndex(int idx) { 130 int prevIdx = getSelectedIndex(); 131 super.setSelectedIndex(idx); 132 // show the actual component for the selected index and change 133 // the other to it's blank 134 if (prevIdx != -1 && idx != -1) { 135 super.setComponentAt(prevIdx, blanks.get(prevIdx)); 136 super.setComponentAt(idx, comps.get(idx)); 137 } 138 } 139 140 @Override 141 public void setSelectedComponent(Component comp) { 142 if (comp == null || comps.indexOf(comp) < 0) { 143 throw new IllegalArgumentException("Component not found in tabbed pane"); 144 } 145 int idx = comps.indexOf(comp); 146 setSelectedIndex(idx); 147 } 148 149 @Override 150 public void removeTabAt(int idx) { 151 super.removeTabAt(idx); 152 comps.remove(idx); 153 blanks.remove(idx); 154 } 155 156 @Override 157 public void remove(int idx) { 158 removeTabAt(idx); 159 } 160 161 @Override 162 public void removeAll() { 163 super.removeAll(); 164 comps.clear(); 165 blanks.clear(); 166 } 167 168 /** 169 * {@code ChangeEvent} are delayed by the heavy weight delay 170 * milliseconds to aid in the proper rendering of heavy weight components. 171 * @see javax.swing.JTabbedPane#fireStateChanged() 172 */ 173 @Override 174 protected void fireStateChanged() { 175 try { 176 Thread.sleep(heavyWeightDelay); 177 } catch (InterruptedException e) {} 178 super.fireStateChanged(); 179 } 180 181// public static void main(String[] args) throws Exception { 182// javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getCrossPlatformLookAndFeelClassName()); 183// JFrame frame = new JFrame("J3DTabbedPane"); 184// frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 185// final JTabbedPane tabs = new HeavyTabbedPane(); 186// frame.setLayout(new BorderLayout()); 187// frame.add(tabs, BorderLayout.CENTER); 188// JPanel panel = new JPanel(); 189// panel.setLayout(new BorderLayout()); 190// panel.setName("BluePanel"); 191// panel.add(new JLabel("Actual"), BorderLayout.BEFORE_FIRST_LINE); 192// panel.setBackground(Color.BLUE); 193// DisplayImpl display = new DisplayImplJ3D("Blue"); 194// panel.add(display.getComponent(), BorderLayout.CENTER); 195// tabs.add("BluePanel", panel); 196// panel = new JPanel(); 197// panel.setLayout(new BorderLayout()); 198// display = new DisplayImplJ3D("Red"); 199// panel.add(display.getComponent(), BorderLayout.CENTER); 200// panel.setName("RedPanel"); 201// panel.add(new JLabel("Actual"), BorderLayout.BEFORE_FIRST_LINE); 202// panel.setBackground(Color.RED); 203// tabs.add("RedPanel", panel); 204// frame.setSize(400, 600); 205// frame.setVisible(true); 206// 207// tabs.addChangeListener(new ChangeListener() { 208// public void stateChanged(ChangeEvent e) { 209// System.err.println(); 210// for (int i=0; i<tabs.getTabCount(); i++) { 211// System.err.println("Tab " + i + " " + tabs.getComponentAt(i).getName()); 212// } 213// } 214// }); 215// } 216}