1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package com.vci.client.ui.swing.components;
 
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Transparency;
 
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
import javax.swing.event.EventListenerList;
 
import com.vci.client.ui.swing.VCISwingUtil;
//import java.awt.geom.AffineTransform;
//import java.util.ArrayList;
//import java.util.HashMap;
/**
* A JTabbedPane which has a close ('X') icon on each tab.
* To add a tab, use the method addTab(String, Component)
* To have an extra icon on each tab (e.g. like in JBuilder,
* showing the file type) use the method
* addTab(String, Component, Icon).
* Only clicking the 'X' closes the tab. */
public class JClosableTabbedPane extends JTabbedPane {
    private static final long serialVersionUID = -5331239697810133531L;
    private EventListenerList closeListenerList = new EventListenerList();
    public JClosableTabbedPane() {
        super();
        setFont(VCISwingUtil.FONT_DEFAULT);
    }
    public void addTab(String title, Component component, boolean closeable) {
        this.addTab(title, component, null, closeable);
    }
    public void addTab(String title, Component component, Icon extraIcon, boolean closeable) {
        this.addTab(title, component, extraIcon, null, closeable);
    }
    
    public void addTab(String title, Component component, Icon extraIcon, String tip, boolean closeable) {
        if (closeable) {
            super.add(title, component);
            int index = getTabCount() - 1;
            TabTitleComponent tabComponent = new TabTitleComponent(this, component);
            tabComponent.setTitle(title);
            this.setTabComponentAt(index, tabComponent);
        } else {
            super.addTab(title, extraIcon, component, tip);
        }
    }
    
    public void addCloseListener(CloseListener l){
        this.closeListenerList.add(CloseListener.class, l);
    }
    
    public void removeCloseListener(CloseListener l){
        if(l != null){
            this.closeListenerList.remove(CloseListener.class, l);
        }
    }
    private final String BEFORECLOSEING = "beforeCloseing";
    private final String AFTERCLOSED = "afterClosed";
    private void invokeBeforeCloseing(int index, Component tabCompt){
        invoke(index, BEFORECLOSEING, tabCompt);
    }
    private void invokeAfterClosed(int index, Component tabCompt){
        invoke(index, AFTERCLOSED, tabCompt);
    }
    
    private void invoke(int index, String method, Component tabCompt){
        CloseListener[] listeners = getCloseListeners();
        if(listeners != null){
            for(CloseListener l : listeners){
                CloseEvent e = new CloseEvent(this, index, tabCompt);
                invoke(l, e, method);
            }
        }
    }
    
    private void invoke(CloseListener l, CloseEvent e, String method){
        if(method.equalsIgnoreCase(BEFORECLOSEING)){
            l.beforeCloseing(e);
        } else if(method.equals(AFTERCLOSED)){
            l.afterClosed(e);
        }
    }
    
    private CloseListener[] getCloseListeners(){
        CloseListener[] listeners = closeListenerList.getListeners(CloseListener.class);
        return listeners;
    }
    
    public void removeTabAt(int index) {
        Component tabCompt = getTabComponentAt(index);
        invokeBeforeCloseing(index, tabCompt);
        super.removeTabAt(index);
        invokeAfterClosed(index, tabCompt);
    }
 
    public static void main(String args[]) {
        try {
            UIManager
                    .setLookAndFeel(VCISwingUtil.LOOK_AND_FEEL_NimbusLookAndFeel);
        } catch (Exception e) {
            e.printStackTrace();
        }
        final JClosableTabbedPane pane = new JClosableTabbedPane();
        for (int i = 0; i < 20; i++) {
            pane.addTab("Title " + String.valueOf(i), new VCIJPanel(), true);
        }
        pane.addCloseListener(new CloseListener() {
            @Override
            public void beforeCloseing(CloseEvent e) {
                // TODO Auto-generated method stub
                VCIJOptionPane.showMessage(pane, "BeforeCloseing:" + String.valueOf(e.getTabIndex()));
            }
            
            @Override
            public void afterClosed(CloseEvent e) {
                // TODO Auto-generated method stub
                VCIJOptionPane.showMessage(pane, "AfterClosed:" + String.valueOf(e.getTabIndex()));
            }
        });
        JFrame frame = new JFrame("Demo");
        frame.getContentPane().add(pane, BorderLayout.CENTER);
        frame.setSize(500, 300);
        frame.setLocation(300, 200);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
}
/**
* The class which generates the 'X' icon for the tabs. The constructor
* accepts an icon which is extra to the 'X' icon, so you can have tabs
* like in JBuilder. This value is null if no extra icon is required.
*/
class CloseTabIcon implements Icon {
    private int x_pos;
    private int y_pos;
    private int width;
    private int height;
    private Icon fileIcon;
    public CloseTabIcon(Icon fileIcon) {
        this.fileIcon = fileIcon;
        width = 16;
        height = 16;
    }
    public void paintIcon(Component c, Graphics g, int x, int y) {
        this.x_pos = x;
        this.y_pos = y;
        if(g instanceof Graphics2D){
            Graphics2D g2d = (Graphics2D)g;
            g2d.getDeviceConfiguration().createCompatibleImage(width*2, height*2, Transparency.TRANSLUCENT);
        }
        Color col = g.getColor();
        g.setColor(Color.black);
        int y_p = y + 2;
        g.drawLine(x + 1, y_p, x + 12, y_p);
        g.drawLine(x + 1, y_p + 13, x + 12, y_p + 13);
        g.drawLine(x, y_p + 1, x, y_p + 12);
        g.drawLine(x + 13, y_p + 1, x + 13, y_p + 12);
        g.drawLine(x + 3, y_p + 3, x + 10, y_p + 10);
        g.drawLine(x + 3, y_p + 4, x + 9, y_p + 10);
        g.drawLine(x + 4, y_p + 3, x + 10, y_p + 9);
        g.drawLine(x + 10, y_p + 3, x + 3, y_p + 10);
        g.drawLine(x + 10, y_p + 4, x + 4, y_p + 10);
        g.drawLine(x + 9, y_p + 3, x + 3, y_p + 9);
        g.setColor(col);
        if (fileIcon != null) {
            fileIcon.paintIcon(c, g, x + width, y_p);
        }
    }
    public int getIconWidth() {
        return width + (fileIcon != null ? fileIcon.getIconWidth() : 0);
    }
    public int getIconHeight() {
        return height;
    }
    public Rectangle getBounds() {
        return new Rectangle(x_pos, y_pos, width, height);
    }
}