1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.gui.component;
21
22 import java.awt.BorderLayout;
23 import java.awt.Component;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.util.Enumeration;
27 import java.util.Hashtable;
28 import java.util.Iterator;
29 import java.util.Vector;
30
31 import javax.swing.Icon;
32 import javax.swing.JComponent;
33 import javax.swing.JPanel;
34 import javax.swing.JTabbedPane;
35 import javax.swing.Timer;
36 import javax.swing.event.ChangeEvent;
37 import javax.swing.event.ChangeListener;
38
39 /***
40 * Provides a container that can display multiple panels. The view can be
41 * switched between a {@link javax.swing.JTabbedPane JTabbedPane} view and
42 * an {@link IconSplitPane} view.
43 */
44 public class ToggleableIconPane extends JPanel implements ChangeListener
45 {
46
47
48
49 public static final int BLINK_INTERVAL = 500;
50
51
52
53 /***
54 * ChangeListeners.
55 */
56 Vector cls = new Vector();
57 IconSplitPane ipane = null;
58 int dividerSize;
59 JTabbedPane tpane = null;
60 boolean useTabbedPane;
61 /***
62 * Maps descriptions to components.
63 */
64 Hashtable componentByDescription = new Hashtable();
65 Timer timer;
66 /***
67 * Always set to non null value to make life easy.
68 */
69 Blinker blinker = new Blinker();
70
71 private Hashtable clientProperties;
72
73
74
75 /***
76 * Constructs a ToggleableIconPane in tabbed mode if tabbed is true and
77 * given dividerSize for the IconSplitPane.
78 *
79 * @param tabbed if true the TabbedPane is the active view, otherwise it's
80 * the IconSplitPane
81 * @param dividerSize the size of the divider for the IconSplitPane view
82 */
83 public ToggleableIconPane(boolean tabbed, int dividerSize)
84 {
85 this.dividerSize = dividerSize;
86
87 setLayout(new BorderLayout());
88 setTabbed(tabbed);
89
90 addChangeListener(this);
91 }
92
93 /***
94 * Constructs a ToggleableIconPane in tabbed mode if tabbed is true.
95 *
96 * @param tabbed if true the TabbedPane is the active view, otherwise it is
97 * the IconSplitPane
98 */
99 public ToggleableIconPane(boolean tabbed)
100 {
101 this(tabbed, -1);
102 }
103
104
105
106
107 /***
108 * Inserts a component, at index, represented by a description and/or
109 * icon.
110 *
111 * @param description the title for this component
112 * @param icon the icon to be displayed representing the component
113 * @param c the component to be displayed when the respective tab/icon is
114 * clicked
115 * @param index the position where the component is inserted
116 */
117 public void insertTab(String description, Icon icon, JComponent c,
118 int index)
119 {
120 componentByDescription.put(description, c);
121
122 if (useTabbedPane)
123 tpane.insertTab(description, icon, c, null, index);
124 else
125 ipane.insertTab(description, icon, c, index);
126
127 if (index <= blinker.index) {
128 blinker.index++;
129 }
130 }
131
132 /***
133 * Adds a component, represented by a description and/or icon.
134 *
135 * @param description the title for this component
136 * @param icon the icon to be displayed representing the component
137 * @param c the component to be displayed when the respective tab/icon is
138 * clicked
139 */
140 public void addTab(String description, Icon icon, JComponent c)
141 {
142 componentByDescription.put(description, c);
143
144 if (useTabbedPane)
145 tpane.addTab(description, icon, c);
146 else
147 ipane.addTab(description, icon, c);
148 }
149
150 /***
151 * Returns all the components in this container.
152 *
153 * @return an array of all child components of this container
154 */
155 public Component[] getTabs()
156 {
157 if (useTabbedPane)
158 return tpane.getComponents();
159 else
160 return ipane.getTabs();
161 }
162
163 /***
164 * Returns the index of the tab/icon for the specified component.
165 *
166 * Returns the index of the tab for the specified component. Returns -1 if
167 * there is no tab for this component.
168 *
169 * @param component - the component for the tab/icon
170 *
171 * @return the first tab which matches this component, or -1 if there is
172 * no tab/icon for this component.
173 */
174 public int indexOfComponent(Component c)
175 {
176 if (useTabbedPane)
177 return tpane.indexOfComponent(c);
178 else
179 return ipane.indexOfComponent(c);
180 }
181
182 /***
183 * Sets the divider size for the IconSplitPane view.
184 *
185 * @param newSize an integer giving the size of the divider in pixels
186 */
187 public void setDividerSize(int newSize)
188 {
189 dividerSize = newSize;
190 }
191
192 /***
193 * Removes the specified component from the ToggleableIconPane.
194 *
195 * @param c the componennt which is to be removed
196 */
197 public void remove(Component c)
198 {
199 Object key = null;
200 for (Enumeration e = componentByDescription.keys();
201 e.hasMoreElements();) {
202 Object o = e.nextElement();
203 if (componentByDescription.get(o) == c) {
204 key = o;
205 break;
206 }
207 }
208 if (key != null) {
209 componentByDescription.remove(key);
210 }
211
212 if (useTabbedPane)
213 tpane.remove(c);
214 else
215 ipane.remove(c);
216
217 blinker.index--;
218 }
219
220 /***
221 * Adds a ChangeListener to this toggableiconpane.
222 *
223 * @param l the changelistener to add
224 */
225 public void addChangeListener(ChangeListener l)
226 {
227 cls.add(l);
228
229 if (useTabbedPane)
230 tpane.addChangeListener(l);
231 else
232 ipane.addChangeListener(l);
233 }
234
235 /***
236 * Removes a ChangeListener from this toggableiconpane.
237 *
238 * @param l the changelistener to remove
239 */
240 public void removeChangeListener(ChangeListener l) {
241 cls.remove(l);
242
243 if (useTabbedPane)
244 tpane.removeChangeListener(l);
245 else
246 ipane.removeChangeListener(l);
247 }
248
249 /***
250 * Returns the icon at index i.
251 *
252 * @param i the index of the item being queried
253 * @return the icon at index i
254 */
255 public Icon getIconAt(int i)
256 {
257 if (useTabbedPane)
258 return tpane.getIconAt(i);
259 else
260 return ipane.getIconAt(i);
261
262 }
263
264 /***
265 * Sets the icon at index i.
266 *
267 * @param i the index of the item being set
268 * @param icon the icon to be set
269 */
270 public void setIconAt(int i, Icon icon)
271 {
272 if (useTabbedPane)
273 tpane.setIconAt(i, icon);
274 else
275 ipane.setIconAt(i, icon);
276 }
277
278 /***
279 * Sets a blinking icon for the component at index i.
280 *
281 * There is only one icon blinking at a time.
282 *
283 * @param i the index of the item being blinked
284 * @param blinkIcon the icon which blinks.
285 */
286 public void blink(int i, Icon blinkIcon)
287 {
288 unblink();
289
290 blinker = new Blinker(i, blinkIcon);
291 timer = new Timer(BLINK_INTERVAL, blinker);
292 timer.start();
293 }
294
295 /***
296 * Stops blinking and restores the original icon for the blinking tab/icon.
297 */
298 public void unblink()
299 {
300 if (timer != null) {
301 timer.stop();
302 setIconAt(blinker.index, blinker.savedIcon);
303 timer = null;
304 }
305 }
306
307 /***
308 * Returns the index of the currently selected component.
309 *
310 * Returns -1 if there is no currently selected component.
311 *
312 * @return the index of the selected component
313 */
314 public int getSelectedIndex()
315 {
316 if (useTabbedPane)
317 return tpane.getSelectedIndex();
318 else
319 return ipane.getSelectedIndex();
320 }
321
322 /***
323 * Returns the currently selected component for this toggableiconpane.
324 *
325 * Returns null if there is no currently selected component.
326 *
327 * @return the component corresponding to the selected tab/icon
328 */
329 public Component getSelectedComponent()
330 {
331 if (useTabbedPane)
332 return tpane.getSelectedComponent();
333 else
334 return ipane.getSelectedComponent();
335 }
336
337 /***
338 * Returns the number of child components.
339 */
340 public int getTabCount()
341 {
342 if (useTabbedPane)
343 return tpane.getTabCount();
344 else
345 return ipane.getTabCount();
346 }
347
348 public void putTabClientProperty(Object key, Object value)
349 {
350 if (clientProperties == null) {
351 clientProperties = new Hashtable();
352 }
353
354 clientProperties.put(key, value);
355 updateClientProperties();
356 }
357
358 /***
359 * Selects the tab/icon having the given description.
360 */
361 public void setSelectedComponent(String description)
362 {
363 setSelectedComponent((Component)componentByDescription.get(description));
364 }
365
366 /***
367 * Selects the given child component.
368 */
369 public void setSelectedComponent(Component c)
370 {
371 if (useTabbedPane)
372 tpane.setSelectedComponent(c);
373 else
374 ipane.setSelectedComponent(c);
375 }
376
377 /***
378 * Toggles between the TabbedPane view and the IconSplitPane view.
379 *
380 * @param newValue whether or not the new view is the tabbed view
381 */
382 public void setTabbed(boolean newValue)
383 {
384 useTabbedPane = newValue;
385
386 if (useTabbedPane && tpane == null) {
387 tpane = new JTabbedPane();
388 if (ipane != null) {
389 super.remove(ipane);
390 Component c = ipane.getSelectedComponent();
391
392 for (Iterator i = cls.iterator(); i.hasNext();) {
393 ChangeListener l = (ChangeListener)i.next();
394 ipane.removeChangeListener(l);
395 tpane.addChangeListener(l);
396 }
397
398 while(ipane.getTabCount() > 0) {
399 String title = ipane.getTitleAt(0);
400 Icon icon = ipane.getIconAt(0);
401 Component component = ipane.getTabAt(0);
402 ipane.removeTabAt(0);
403 tpane.addTab(title, icon, component);
404 }
405
406 ipane = null;
407 tpane.setSelectedComponent(c);
408 }
409 super.add(tpane, BorderLayout.CENTER);
410 }
411 else if (!useTabbedPane && ipane == null) {
412 ipane = new IconSplitPane();
413 if (dividerSize != -1) {
414 ipane.setDividerSize(dividerSize);
415 }
416 if (tpane != null) {
417 super.remove(tpane);
418 Component c = tpane.getSelectedComponent();
419
420 for (Iterator i = cls.iterator(); i.hasNext();) {
421 ChangeListener l = (ChangeListener)i.next();
422 tpane.removeChangeListener(l);
423 ipane.addChangeListener(l);
424 }
425
426 while(tpane.getTabCount() > 0) {
427 String title = tpane.getTitleAt(0);
428 Icon icon = tpane.getIconAt(0);
429 Component component = tpane.getComponentAt(0);
430 tpane.removeTabAt(0);
431 ipane.addTab(title, icon, component);
432 }
433
434 tpane = null;
435 ipane.setSelectedComponent(c);
436 }
437 super.add(ipane, BorderLayout.CENTER);
438 }
439
440 updateClientProperties();
441 }
442
443 /***
444 * Implements the {@link ChangeListener} interface.
445 */
446 public void stateChanged(ChangeEvent e)
447 {
448 if (timer != null && getSelectedIndex() == blinker.index) {
449 unblink();
450 }
451 }
452
453 protected void updateClientProperties()
454 {
455 if (clientProperties != null) {
456 for (Iterator i = clientProperties.keySet().iterator(); i.hasNext();) {
457 Object key = i.next();
458 if (useTabbedPane)
459 tpane.putClientProperty(key, clientProperties.get(key));
460 else
461 ipane.putClientProperty(key, clientProperties.get(key));
462 }
463 }
464 }
465
466 protected class Blinker implements ActionListener
467 {
468 public Icon blinkIcon;
469 public Icon savedIcon;
470 public int index = 0;
471
472 public Blinker(int index, Icon blinkIcon)
473 {
474 this.index = index;
475 this.blinkIcon = blinkIcon;
476 savedIcon = getIconAt(index);
477 }
478
479 public Blinker()
480 {
481 }
482
483 public void actionPerformed(ActionEvent e)
484 {
485 Icon icon = (getIconAt(index) == savedIcon)
486 ? blinkIcon : savedIcon;
487 setIconAt(index, icon);
488 }
489 }
490
491 }