001 /* 002 * LAPIS lightweight structured text processing system 003 * 004 * Copyright (C) 1998-2002 Carnegie Mellon University, 005 * Copyright (C) 2003 Massachusetts Institute of Technology. 006 * All rights reserved. 007 * 008 * This library is free software; you can redistribute it 009 * and/or modify it under the terms of the GNU General 010 * Public License as published by the Free Software 011 * Foundation, version 2. 012 * 013 * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/ 014 */ 015 016 package lapisx.swing; 017 018 import java.awt.*; 019 import java.awt.event.*; 020 import java.util.*; 021 import javax.swing.*; 022 023 024 /** 025 * A wrapper class for JTabbedPane that hides the tabs when there 026 * is only one tab. Otherwise a similar interface to JTabbedPane. 027 */ 028 public class DisappearingTabbedPane extends JPanel { 029 public static lapisx.util.Debug debug = lapisx.util.Debug.QUIET; 030 031 /** 032 * Map from tab component to tab title. 033 */ 034 Map children; 035 036 /** 037 * State pattern. One of EMPTY, ONE_TAB, or MULTIPLE_TABS. 038 */ 039 InternalState state; 040 041 /** 042 * Panel shown for 0 or 1 tabs. 043 */ 044 JPanel singleton; 045 046 /** 047 * Tabbed pane, shown for more than 1 tab. 048 */ 049 JTabbedPane tabs; 050 051 /** 052 * Layout for this. Flips between singleton (used in EMPTY and ONE_TAB 053 * states) and tabs (in MULTIPLE_TABS state). 054 */ 055 CardLayout cards; 056 057 058 059 /** 060 * Make a DisappearingTabbedPane with same defaults as new JTabbedPane(). 061 */ 062 public DisappearingTabbedPane () { 063 this (new JTabbedPane ()); 064 } 065 066 /** 067 * Make a DisappearingTabbedPane with given tab placement. Otherwise, 068 * same defaults as new JTabbedPane(tabPlacement). 069 * 070 * @param tabPlacement one of SwingConstants.TOP, BOTTOM, LEFT, RIGHT 071 */ 072 public DisappearingTabbedPane (int tabPlacement) { 073 this (new JTabbedPane (tabPlacement)); 074 } 075 076 /** 077 * Make a DisappearingTabbedPane that encapsulates the given JTabbedPane. 078 * 079 * @param tabs JTabbedPane that will be added to the new 080 * DisappearingTabbedPane 081 * @requires don't use the JTabbedPane object after putting it under 082 * DisappearingTabbedPane's control 083 */ 084 public DisappearingTabbedPane (JTabbedPane tabs) { 085 this.tabs = tabs; 086 087 state = EMPTY; 088 children = new HashMap (); 089 090 setLayout (cards = new CardLayout ()); 091 092 super.add ("singleton", singleton = new JPanel ()); 093 singleton.setLayout (new CardLayout ()); 094 095 super.add ("tabs", tabs); 096 } 097 098 /** 099 * Add a tab. 100 * 101 * @param title Title shown on tab 102 * @param comp Component displayed in tab 103 */ 104 public void addTab (String title, Component comp) { 105 debug.println("attempting to add new component to Tabbed pane"); 106 if (children.containsKey (comp)){ 107 debug.println("component exists, removing"); 108 remove (comp); 109 } 110 state.addTab (title, comp); 111 debug.println("added new component. tabs: " + getTabCount()); 112 } 113 114 /** 115 * returns true if pane already exists in tabbed pane 116 **/ 117 118 public boolean containsComp(Component pane) { 119 return children.containsKey (pane); 120 } 121 122 123 /** 124 * Remove a tab. 125 * 126 * @param comp Component to remove 127 */ 128 public void remove (Component comp) { 129 if (!children.containsKey (comp)){ 130 debug.println("component exists, removing"); 131 return; 132 } 133 state.removeTab (comp); 134 debug.println("component removed. tabs: " + getTabCount()); 135 136 } 137 138 /** 139 * Select a tab (bringing it to top). 140 * 141 * @param comp Component to select 142 */ 143 public void setSelectedComponent (Component comp) { 144 state.selectTab (comp); 145 } 146 147 /** 148 * Get the selected tab (returns null if there is no tab). 149 * 150 * @return component contained in the currently selected 151 * tab 152 */ 153 public Component getSelectedComponent () { 154 return state.getSelectedTab (); 155 } 156 157 /** 158 * Get number of tabs. 159 * 160 * @return number of tabs in this 161 */ 162 public int getTabCount () { 163 return children.size (); 164 } 165 166 167 /** 168 * Flip between one-tab and multiple-tab display. 169 * 170 * @param card "singleton" for singleton tab, "tabs" for tabbed pane 171 */ 172 void flipTo (String card) { 173 cards.show (this, card); 174 } 175 176 /** 177 * State pattern. 178 */ 179 static interface InternalState { 180 void addTab (String title, Component comp); 181 void removeTab (Component comp); 182 void selectTab (Component comp); 183 Component getSelectedTab (); 184 } 185 186 /** 187 * State in which this contains no tabs. 188 */ 189 final InternalState EMPTY = new InternalState () { 190 public void addTab (String title, Component comp) { 191 debug.assertion (children.size () == 0); 192 debug.assertion (!children.containsKey (comp)); 193 194 singleton.add (title, comp); 195 flipTo ("singleton"); 196 197 children.put (comp, title); 198 199 state = ONE_TAB; 200 } 201 public void removeTab (Component comp) { 202 debug.assertion (false); // shouldn't get here 203 } 204 public void selectTab (Component comp) { 205 debug.assertion (false); // shouldn't get here 206 } 207 public Component getSelectedTab () { 208 return null; 209 } 210 }; 211 212 /** 213 * State in which this contains exactly one tab. 214 */ 215 final InternalState ONE_TAB = new InternalState () { 216 public void addTab (String title, Component comp) { 217 debug.assertion (children.size () == 1); 218 debug.assertion (!children.containsKey (comp)); 219 220 Component firstComp = 221 (Component) children.keySet ().iterator ().next (); 222 String firstTitle = 223 (String) children.get (firstComp); 224 225 tabs.addTab (firstTitle, firstComp); 226 tabs.addTab (title, comp); 227 flipTo ("tabs"); 228 229 children.put (comp, title); 230 debug.assertion (children.size () == 2); 231 232 state = MULTIPLE_TABS; 233 } 234 public void removeTab (Component comp) { 235 debug.assertion (children.containsKey (comp)); 236 children.remove (comp); 237 singleton.remove (comp); 238 state = EMPTY; 239 } 240 public void selectTab (Component comp) { 241 debug.assertion (children.containsKey (comp)); 242 // don't have to do anything, because singleton tab is always 243 // selected 244 } 245 public Component getSelectedTab () { 246 return tabs.getSelectedComponent(); 247 } 248 }; 249 250 /** 251 * State in which this contains more than one tab. 252 */ 253 final InternalState MULTIPLE_TABS = new InternalState () { 254 public void addTab (String title, Component comp) { 255 debug.assertion (children.size () > 1); 256 debug.assertion (!children.containsKey (comp)); 257 258 tabs.addTab (title, comp); 259 children.put (comp, title); 260 } 261 public void removeTab (Component comp) { 262 debug.assertion (children.containsKey (comp)); 263 children.remove (comp); 264 tabs.remove (comp); 265 266 if (children.size () == 1) { 267 Component firstComp = 268 (Component) children.keySet ().iterator ().next (); 269 String firstTitle = 270 (String) children.get (firstComp); 271 272 singleton.add (firstTitle, firstComp); 273 flipTo ("singleton"); 274 275 state = ONE_TAB; 276 } 277 } 278 public void selectTab (Component comp) { 279 debug.assertion (children.containsKey (comp)); 280 tabs.setSelectedComponent (comp); 281 } 282 public Component getSelectedTab () { 283 return tabs.getSelectedComponent(); 284 } 285 }; 286 287 288 static int tabsCreated = 0; 289 290 public static void main (String[] args) { 291 final DisappearingTabbedPane tabs = new DisappearingTabbedPane (); 292 final JToolBar toolbar = new JToolBar (); 293 final JFrame f = new JFrame ("DisappearingTabbedPane"); 294 Container c = f.getContentPane (); 295 c.add (tabs, BorderLayout.CENTER); 296 c.add (toolbar, BorderLayout.SOUTH); 297 298 toolbar.add (new AbstractAction ("Add Tab") { 299 public void actionPerformed (ActionEvent event) { 300 String title = "Tab " + (tabsCreated++); 301 final JLabel comp = new JLabel (title); 302 tabs.addTab (title, comp); 303 tabs.setSelectedComponent (comp); 304 toolbar.add (new AbstractAction ("Remove " + title) { 305 public void actionPerformed (ActionEvent event) { 306 tabs.remove (comp); 307 toolbar.remove ((Component) event.getSource ()); 308 f.pack (); 309 } 310 }); 311 f.pack (); 312 } 313 }); 314 315 f.addWindowListener (new WindowAdapter () { 316 public void windowClosing (WindowEvent event) { 317 System.exit(0); 318 } 319 }); 320 f.pack (); 321 f.show (); 322 } 323 324 }