001: package rcm.awt;
002:
003: import java.awt.*;
004: import java.awt.event.*;
005:
006: public class TabPanel extends Panel {
007: static final int MAX_TABS = 25;
008: static final int selUpper = 2;
009: static final int lineWidth = 2;
010: static final int horRound = 2;
011: static final int verRound = 2;
012: static final int internalMargin = 4;
013: static final int xTabOffset = 2;
014: static final int XTitle = 4;
015: static final int YTitle = 2;
016:
017: Font plainFont; // computed by recalculate ()
018: Font boldFont; // computed by recalculate ()
019: FontMetrics fmBoldFont; // computed by recalculate ()
020:
021: final Color light = new Color(223, 223, 223);
022: final Color shadow = new Color(127, 127, 127);
023:
024: CardLayout tabLayout = new CardLayout();
025: Panel tabPanel = new Panel();
026:
027: boolean mustRecalculate = true;
028:
029: int Z; // computed by recalculate ()
030:
031: int W;
032: int H;
033:
034: Component cards[] = new Component[MAX_TABS];
035: String arrName[] = new String[MAX_TABS];
036: int arrEnd[] = new int[MAX_TABS]; // computed by recalculate ()
037: int arrBeg[] = new int[MAX_TABS]; // computed by recalculate ()
038:
039: int nbTab = 0;
040: int selected = -1;
041:
042: // Extra feature: zero or more buttons flush-right on tab line
043: final static int BUTTON_GUTTER = 2; // pixels between buttons
044:
045: public TabPanel() {
046: setLayout(null);
047: tabPanel.setLayout(tabLayout);
048: add(tabPanel);
049: addMouseListener(new MouseHandler());
050: }
051:
052: void recalculate() {
053: plainFont = getFont();
054: if (plainFont == null)
055: plainFont = new Font("Helvetica", Font.PLAIN, 12);
056: boldFont = new Font(plainFont.getFamily(), Font.BOLD, plainFont
057: .getSize());
058: fmBoldFont = getFontMetrics(boldFont);
059:
060: Z = fmBoldFont.getHeight() + 2 * lineWidth + 2 * YTitle + 1;
061:
062: for (int i = 0; i < nbTab; ++i) {
063: arrBeg[i] = (i == 0) ? xTabOffset : arrEnd[i - 1];
064: arrEnd[i] = arrBeg[i] + 2 * XTitle
065: + fmBoldFont.stringWidth(arrName[i]) + 2
066: * lineWidth;
067:
068: }
069:
070: if (selected >= 0) {
071: arrBeg[selected] -= lineWidth;
072: arrEnd[selected] += lineWidth;
073: }
074:
075: mustRecalculate = false;
076: }
077:
078: public void layout() {
079: if (mustRecalculate)
080: recalculate();
081: Dimension external = getSize();
082: W = external.width;
083: H = external.height;
084:
085: int x = lineWidth + internalMargin;
086: int y = Z + 1 + internalMargin;
087: int w = W - 2 * (lineWidth + internalMargin);
088: int h = H - Z - 2 * (lineWidth + internalMargin);
089:
090: tabPanel.setBounds(x, y, w, h);
091: tabPanel.validate();
092:
093: Component[] c = getComponents();
094: int bx = external.width;
095: for (int i = c.length - 1; i >= 0; --i)
096: if (c[i] != tabPanel && c[i].isVisible()) {
097: Dimension d = c[i].getPreferredSize();
098: bx -= d.width;
099: c[i].setBounds(bx, 0, d.width, d.height);
100: bx -= BUTTON_GUTTER;
101: }
102: }
103:
104: public Dimension getPreferredSize() {
105: Dimension d = super .getPreferredSize();
106: return new Dimension(
107: d.width + 2 * (lineWidth + internalMargin), d.height
108: + Z + 2 * (lineWidth + internalMargin));
109: }
110:
111: public Dimension getMinimumSize() {
112: Dimension d = super .getMinimumSize();
113: return new Dimension(
114: d.width + 2 * (lineWidth + internalMargin), d.height
115: + Z + 2 * (lineWidth + internalMargin));
116: }
117:
118: public void addTabPanel(String name, boolean enabled, Component c) {
119: if (c.getParent() == tabPanel // panel already added
120: || nbTab >= arrBeg.length // too many tabs
121: || c == this ) // can't add self
122: return;
123:
124: cards[nbTab] = c;
125: arrName[nbTab] = name;
126: tabPanel.add(String.valueOf(nbTab), c);
127: nbTab++;
128:
129: if (selected < 0)
130: select(0);
131:
132: mustRecalculate = true;
133: repaint();
134: }
135:
136: public String[] getPanelLabels() {
137: return arrName;
138: }
139:
140: public void removeAllTabPanels() {
141: for (int i = nbTab - 1; i >= 0; --i)
142: removeTabPanel(i);
143: }
144:
145: public void removeTabPanel(Component c) {
146: //finding the tab
147: for (int i = 0; i < cards.length; i++) {
148: if (cards[i] == c) {
149: removeTabPanel(i);
150: break;
151: }
152: }
153: }
154:
155: public void removeTabPanel(int i) {
156: if (i < 0 || i >= nbTab)
157: return;
158: tabPanel.remove(cards[i]);
159: for (int j = i; j < nbTab - 1; j++) {
160: arrName[j] = arrName[j + 1];
161: cards[j] = cards[j + 1];
162: }
163: nbTab--;
164: if (selected == i)
165: select(Math.min(selected + 1, nbTab - 1));
166: mustRecalculate = true;
167: repaint();
168: }
169:
170: public int countTabs() {
171: return nbTab;
172: }
173:
174: public void renameTab(String oldName, String newName) {
175: for (int i = 0; i < nbTab; i++) {
176: if (arrName[i].equals(oldName)) {
177: arrName[i] = new String(newName);
178: mustRecalculate = true;
179: repaint();
180: break;
181: }
182: }
183: }
184:
185: public void select(int num) {
186: if (num < 0 || num > nbTab || num == selected)
187: return;
188: selected = num;
189: tabLayout.show(tabPanel, Integer.toString(selected));
190: mustRecalculate = true;
191: repaint();
192: }
193:
194: public Component getSelectedComponent() {
195: if (selected < 0 || selected >= nbTab)
196: return null;
197: else
198: return cards[selected];
199: }
200:
201: public synchronized void update(Graphics g) {
202: // avoid clearing background
203: paint(g); // FIX: what happens if a component leaves garbage behind?
204: }
205:
206: public synchronized void paint(Graphics g) {
207: if (mustRecalculate || getFont() != plainFont)
208: recalculate();
209:
210: Dimension d = getSize();
211: g.setColor(getBackground());
212: g.fillRect(0, 0, W, Z + 1);
213:
214: for (int curr = 0; curr < nbTab; curr++) {
215: int upper = (curr == selected) ? 0 : selUpper;
216: g.setFont((curr == selected) ? boldFont : plainFont);
217:
218: g.setColor(shadow);
219: for (int i = lineWidth; curr != (selected - 1) && i > 0; i--) {
220: if (i == 1)
221: g.setColor(Color.black);
222: g.drawLine(arrEnd[curr] - i, upper + verRound,
223: arrEnd[curr] - i, Z - lineWidth);
224: if (i == 1)
225: g.drawLine(arrEnd[curr] - lineWidth, upper + 1,
226: arrEnd[curr] - lineWidth, upper + 1);
227: }
228:
229: g.setColor(Color.black);
230: g.drawString(arrName[curr], arrBeg[curr]
231: + (curr == selected ? 2 * lineWidth : lineWidth)
232: + XTitle, upper + YTitle + lineWidth
233: + fmBoldFont.getAscent());
234:
235: g.setColor(Color.white);
236: for (int i = 0; i < lineWidth; i++) {
237: if (i == 1)
238: g.setColor(light);
239: g.drawLine(arrBeg[curr] + horRound, upper + i,
240: arrEnd[curr] - lineWidth - horRound + 1, upper
241: + i);
242: if (curr != (selected + 1)) {
243: g.drawLine(arrBeg[curr] + i, upper + verRound,
244: arrBeg[curr] + i, Z);
245: if (i == 0)
246: g.drawLine(arrBeg[curr] + 1, upper + 1,
247: arrBeg[curr] + 1, upper + 1);
248: }
249: }
250: }
251:
252: g.setColor(Color.white);
253: for (int i = 0; i < lineWidth; i++) {
254: if (i == 1)
255: g.setColor(light);
256: g.drawLine(i, Z, i, H - 1);
257: if (selected >= 0) {
258: if (selected != 0)
259: g.drawLine(0, Z - lineWidth + i + 1,
260: arrBeg[selected], Z - lineWidth + i + 1);
261: g.drawLine(arrEnd[selected], Z - lineWidth + i + 1,
262: W - 1, Z - lineWidth + i + 1);
263: } else {
264: g.drawLine(0, Z - lineWidth + i + 1, W - 1, Z
265: - lineWidth + i + 1);
266: }
267: }
268:
269: g.setColor(shadow);
270: for (int i = lineWidth; i > 0; i--) {
271: if (i == 1)
272: g.setColor(Color.black);
273: g.drawLine(0, H - i, W, H - i);
274: g.drawLine(W - i, Z, W - i, H - 1);
275: }
276:
277: }
278:
279: void clickTab(int x, int y) {
280: for (int i = 0; i < nbTab; i++)
281: if (x > arrBeg[i] && x < arrEnd[i])
282: select(i);
283: }
284:
285: class MouseHandler extends MouseAdapter {
286: public void mousePressed(MouseEvent event) {
287: if (event.getY() < Z)
288: clickTab(event.getX(), event.getY());
289: }
290: }
291:
292: }
|