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 }