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  
23  import java.awt.Component;
24  import java.awt.Graphics;
25  import java.awt.Rectangle;
26  import java.awt.event.MouseAdapter;
27  import java.awt.event.MouseEvent;
28  
29  import javax.swing.Icon;
30  import javax.swing.JTabbedPane;
31  import javax.swing.SwingUtilities;
32  import javax.swing.UIManager;
33  
34  import org.xnap.gui.util.IconHelper;
35  
36  /***
37   * Provides a <code>JTabbedPane</code> with close icons in the tab titles. 
38   * If an icon is clicked the tab is removed from the pane.
39   */
40  public class CloseableTabbedPane extends JTabbedPane
41  {
42      
43      //--- Data field(s) ---
44  
45      private Icon closeIcon;
46      
47      //--- Constructor(s) ---
48      
49      /***
50       * Constructs a pane.
51       *
52       * @param closeIcon the icon to use in the tab titles
53       */
54      public CloseableTabbedPane(Icon closeIcon) 
55      {
56  		this.closeIcon = closeIcon;
57  
58  		addMouseListener(new ClosingListener());
59      }
60      
61  	/***
62       * Constructs a pane with the default icon.
63       *
64       * @see #CloseableTabbedPane(Icon)
65       */
66      public CloseableTabbedPane() 
67      {
68  		this(IconHelper.getTabTitleIcon("remove.png"));
69      }
70  
71      //--- Method(s) ---
72  
73  	public void addTab(String title, Component component, Icon icon, 
74  					   boolean closeable) 
75      {
76  		if (closeable) {
77  			super.addTab(null, new WrapperIcon(title, icon), component);
78  		}
79  		else {
80  			super.addTab(title, icon, component);
81  		}
82  		setSelectedComponent(component);
83      }
84  
85  	public void addTab(String title, Component component, Icon icon)
86  	{
87  		addTab(title, component, icon, true);
88  	}
89  
90      public void addTab(String title, Component component) 
91      {
92  		addTab(title, component, null, true);
93      }
94  
95  	public void setTitleAt(int index, String newTitle)
96  	{
97  		WrapperIcon icon = (WrapperIcon)getIconAt(index);
98  		if (icon != null) {
99  			icon.setTitle(newTitle);
100 			revalidate();
101 			repaint();
102 		}
103 		else {
104 			super.setTitleAt(index, newTitle);
105 		}
106 	}
107 
108 
109     //--- Inner Class(es) ---
110 
111     
112     protected class ClosingListener extends MouseAdapter
113     {
114 	
115 		public void mouseReleased(MouseEvent e)
116 		{
117 			int i = getSelectedIndex();
118 	    
119 			// nothing selected
120 			if (i == -1) {
121 				return;
122 			}
123 
124 			WrapperIcon icon = (WrapperIcon)getIconAt(i);
125 	    
126 			// close tab, if icon was clicked
127 			if (icon != null && icon.contains(e.getX(), e.getY())) {
128 				removeTabAt(i);
129 			}
130 		}
131 	
132     }
133 
134     /***
135      * Acts as a proxy class for the closing icon. 
136      */
137     protected class WrapperIcon implements Icon
138     {
139 
140 		//--- Data field(s) ---
141 
142 		private Icon leftIcon;
143 		private EventIcon rightIcon;
144 		private String title;
145 		private int x = 0;
146 		private int y = 0;
147 		private int height = 10;
148 		private int width = 10;
149 
150 		//--- Constructor(s) ---
151 	
152 		/***
153 		 * 
154 		 */
155 		public WrapperIcon(String title)
156 		{
157 			this(title, null);
158 		}
159 
160 		public WrapperIcon(String title, Icon leftIcon)
161 		{
162 			this.title = title;
163 			this.leftIcon = leftIcon;
164 
165 			rightIcon = new EventIcon(closeIcon);
166 
167 			height = rightIcon.getIconHeight();
168 			width = rightIcon.getIconWidth();
169 		}
170 	
171 		//--- Method(s) ---
172 
173 		public void setTitle(String newTitle)
174 		{
175 			title = newTitle;
176 		}
177 
178 		/***
179 		 *
180 		 */
181 		public int getIconHeight()
182 		{
183 			return height;
184 		}
185 
186 		/***
187 		 * 
188 		 */
189 		public int getIconWidth()
190 		{
191 			int textWidth= SwingUtilities.computeStringWidth
192 				(CloseableTabbedPane.this.getFontMetrics(CloseableTabbedPane.this.getFont()), 
193 				 title);
194 			if (leftIcon != null) {
195 				return leftIcon.getIconWidth() + rightIcon.getIconWidth()
196 					+ textWidth + 16;
197 			}
198 			else {
199 				return textWidth + 8 + rightIcon.getIconWidth();
200 			}
201 		}
202 
203 		/***
204 		 * Overwrites paintIcon to get hold of the coordinates of the icon.
205 		 */
206 		public void paintIcon(Component c, Graphics g, int x, int y)
207 		{
208 			this.x = x + getIconWidth() - rightIcon.getIconWidth();
209 			this.y = y;
210 
211 			// compute the correct y coordinate where to put the text
212 			Rectangle rect = new Rectangle(x, y, getIconWidth(), 
213 										   getIconHeight());
214 			Rectangle iconRect = new Rectangle();
215 			Rectangle textRect = new Rectangle();
216 			SwingUtilities.layoutCompoundLabel
217 				(CloseableTabbedPane.this, g.getFontMetrics(),
218 				 title, rightIcon, SwingUtilities.CENTER,
219 				 SwingUtilities.CENTER,
220 				 SwingUtilities.CENTER,
221 				 SwingUtilities.TRAILING,
222 				 rect, iconRect, textRect,
223 				 UIManager.getInt("TabbedPane.textIconGap"));
224 
225 			if (leftIcon != null) {
226 				leftIcon.paintIcon(c, g, x, y + 1);
227 				g.setFont(CloseableTabbedPane.this.getFont());
228 				g.drawString(title, x + leftIcon.getIconWidth() + 8,
229 							 textRect.y + g.getFontMetrics().getAscent());
230 				rightIcon.paintIcon
231 					(c, g, x + getIconWidth() - rightIcon.getIconWidth(),
232 					 y + 1);
233 			}
234 			else {
235 				g.drawString(title, x,
236 							 textRect.y + g.getFontMetrics().getAscent());
237 				rightIcon.paintIcon
238 					(c, g, x + getIconWidth() - rightIcon.getIconWidth(),
239 					 y + 1);
240 			}
241 		}    
242 	
243 		/***
244 		 * Verifies if x and y are within the icon's borders.
245 		 */
246 		public boolean contains(int xEvent, int yEvent)
247 		{
248 			return rightIcon.contains(xEvent, yEvent);
249 		}
250 	}
251 
252     /***
253      * Acts as a proxy class for the closing icon. 
254      *
255      * <p>The idea for this class stems from limewire's CancelSearchIconProxy
256      * class, thanks for going open source guys.
257      */
258     protected class EventIcon implements Icon
259     {
260 
261 		//--- Data field(s) ---
262 
263 		private Icon icon;    
264 		private int x = 0;
265 		private int y = 0;
266 		private int height = 10;
267 		private int width = 10;
268 
269 		//--- Constructor(s) ---
270 	
271 		public EventIcon(Icon icon)
272 		{
273 			this.icon = icon;
274 
275 			if (icon != null) {
276 				height = icon.getIconHeight();
277 				width = icon.getIconWidth();
278 			}
279 		}
280 	
281 		//--- Method(s) ---
282 
283 		/***
284 		 *
285 		 */
286 		public int getIconHeight()
287 		{
288 			return height;
289 		}
290 
291 		/***
292 		 * 
293 		 */
294 		public int getIconWidth()
295 		{
296 			return width;
297 		}
298 
299 		/***
300 		 * Overwrites paintIcon to get hold of the coordinates of the icon,
301 		 * this is a rather rude approach just to find out if the icon
302 		 * was pressed.
303 		 */
304 		public void paintIcon(Component c, Graphics g, int x, int y)
305 		{
306 			this.x = x;
307 			this.y = y;
308 	    
309 			if (icon != null) {
310 				icon.paintIcon(c, g, x, y);
311 			}
312 			else {
313 				// top left -> bottom right
314 				g.drawLine(x, y, x + width, y + height);
315 
316 				// bottom left -> top right
317 				g.drawLine(x, y + height, x + width, y);
318 			}
319 		}    
320 	
321 		/***
322 		 * Verifies if x and y are within the icon's borders.
323 		 */
324 		public boolean contains(int xEvent, int yEvent)
325 		{
326 			if (!(xEvent >= x) || !(xEvent <= x + width)) {
327 				return false;
328 			}
329 			if (!(yEvent >= y) || !(yEvent <= y + height)) {
330 				return false;
331 			}
332 
333 			return true;
334 		}
335     }
336 
337 }