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
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
44
45 private Icon closeIcon;
46
47
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
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
110
111
112 protected class ClosingListener extends MouseAdapter
113 {
114
115 public void mouseReleased(MouseEvent e)
116 {
117 int i = getSelectedIndex();
118
119
120 if (i == -1) {
121 return;
122 }
123
124 WrapperIcon icon = (WrapperIcon)getIconAt(i);
125
126
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
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
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
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
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
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
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
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
314 g.drawLine(x, y, x + width, y + height);
315
316
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 }