View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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      //--- Constant(s) ---
48  
49      public static final int BLINK_INTERVAL = 500;
50  
51      //--- Data field(s) ---
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      //--- Constructor(s) ---
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     //--- Method(s) ---
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 }