KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > chateverywhere > TabPanel


1 /*****************************************************************************
2  gui.TabPanel
3
4  Container for a set of Tab Cards or Tab Folders.
5
6  bruce.miller@nist.gov
7  Contribution of the National Institute of Standards and Technology,
8  not subject to copyright.
9  
10  
11  Some modifications done by A. de Bernis <alexis@bernis.org> for
12  implementations in Chat Everywhere. All these modifications are put
13  under the General Public License.
14  *****************************************************************************/

15
16 package org.chateverywhere;
17 import java.applet.*;
18 import java.awt.*;
19 import java.util.*;
20
21 /** **************************************************************************
22  TabPanel is a container for a set of tabbed cards, lying atop each other,
23  but with the labelled tabs exposed at the top. That is, the classic Tab
24  Folder. Each card is an awt.component of whatever design you wish. The
25  topmost card can be selected programmatically (Using first(), last(),
26  next(), previous(), or show(name)), or by clicking on the tab with the mouse.
27 <P>
28  Components should be added using add(name,component)); the name is used
29  to label the tab. If you set the layout manager, it should be a subclass
30  of CardLayout.
31  You probably want to setBackground() to a color contrasting that of
32  the parent and the components.
33 <P>
34  Whenever a card is selected (whether by software or mouse), an event with
35  id = Event.WINDOW_EXPOSE is sent to the selected component. Handling this
36  event may be useful for deferred initialization.
37
38 @author Bruce R. Miller (bruce.miller@nist.gov)
39 @author Contribution of the National Institute of Standards and Technology,
40 @author not subject to copyright.
41 */

42 public class TabPanel extends Panel
43 {
44     /** The width of the margins around the cards. */
45     public int margin = 3; // width of margins around cards
46

47     Font tabFont; // for tab labels
48
FontMetrics metric;
49     int nCards = 0; // total # of cards
50
Vector names = new Vector(10,10); // contains the (interned) card names
51
int pos[], width[]; // position & width of each tab
52
int selected = 0; // index of selected (displayed) card
53
int offset = 0; // left shift to allow for `too many' tabs
54
int tabH; // height of tab (set from tabFont)
55
int tabN = 12; // #points along edges: must = (2*int + 2)
56
int tabLeft[][] = new int[2][tabN]; // coordinates of tab edge curves
57
int tabRight[][] = new int[2][tabN];
58     Image offscreen=null;
59
60     /* Creates an empty TabPanel. */
61     public TabPanel()
62     {
63         setLayout(new CardLayout());
64         setTabFont(new Font("Helvetica",Font.BOLD,12));
65     }
66
67     /* internals */
68     int findComponent(Component c)
69     { // find index of a given component
70
for (int i=0; i<nCards; i++)
71             if (getComponent(i) == c)
72                 return i;
73
74         return -1;
75     }
76
77
78     /*
79      * Adding & Removing components
80      */

81
82     /* Add a card, component, to the TabPanel with a given name. */
83     public Component add(String JavaDoc name, Component component)
84     {
85         name = name.intern();
86         super.add(name,component); // Let layout manager do its job
87

88         if (!names.contains(name)) { // if name isn't already present
89
names.addElement(name); // but record our part
90
nCards++;
91             if(isShowing()) { // already showing? better rebuild!
92
computeTabs(); repaint();
93             }
94         }
95
96         return component;
97     }
98
99
100
101     /** remove the card, component, from the TabPanel. */
102     public void remove(Component component)
103     {
104         int i = findComponent(component);
105
106         super.remove(component); // Let layout manager do its job
107
names.removeElementAt(i); // but we'll record our part.
108
nCards--;
109         if (i < selected) {
110             setSelected(selected-1,true);
111         } else if ((i == selected) && (nCards > 0)) { // was selected, select another
112
setSelected(selected % nCards,true);
113         }
114
115         if (isShowing()) { // already showing? better rebuild!
116
computeTabs();
117             repaint();
118         }
119     }
120
121     /** remove the card having the given name from the TabPanel. */
122     public void remove(String JavaDoc name)
123     {
124         int i = names.indexOf(name.intern());
125
126         if (i != -1)
127             remove(getComponent(i));
128     }
129
130     /** remove all cards from the TabPanel. */
131     public void removeAll()
132     {
133         super.removeAll();
134         names.removeAllElements();
135         repaint();
136     }
137
138     /*
139      * Component Selection
140      */

141     void setSelected(int i, boolean force)
142     {
143         if (force || ((i != selected) && (i >= 0) && (i < nCards))) {
144             if (nCards > 0) {
145                 selected = i % nCards;
146             }
147
148             ((CardLayout) getLayout()).show(this, (String JavaDoc) names.elementAt(i));
149             repaint();
150             Component c = getComponent(i);
151             c.postEvent(new Event(c,Event.WINDOW_EXPOSE,this));
152         }
153     }
154
155     /** Select the first card in the Panel. */
156     public void first() { setSelected(0,false); }
157
158     /** Select the last card in the Panel. */
159     public void last() { setSelected(nCards-1,false); }
160
161     /** Select the next card in the Panel. */
162     public void next() { setSelected((selected+1) % nCards,false);}
163
164     /** Select the previous card in the Panel. */
165     public void previous() { setSelected((selected-1+nCards) % nCards,false); }
166
167     /** Select the named card in the Panel. */
168     public void show(String JavaDoc name) {
169       setSelected(names.indexOf(name.intern()),false); }
170
171     /** Select the card component in the Panel. */
172     public void show(Component component)
173     {
174         setSelected(findComponent(component),false);
175     }
176
177     int cardAt(int x, int y)
178     {
179         if (y <= tabH) { // inside tab section?
180
x += offset;
181
182             for(int i = 0; i < nCards; i++)
183                 if ((pos[i]<=x) && (x<pos[i+1]))
184                     return i;
185         }
186         return -1;
187     }
188
189   /** Return a mouse documentation string for selecting this card.
190     * (ie. the applet status line (if there is an applet), for when
191     * the mouse is over the tab).
192     * This may be overridden by a subclass, if desired. The default
193     * is to use the "Select tab card " + name. */

194     public String JavaDoc documentCard(String JavaDoc name)
195     {
196         return "Select tab card " + name;
197     }
198
199     Applet applet = null;
200
201     /** Handle mouse clicks and documentation line for Tab selection. */
202     public boolean handleEvent(Event e)
203     {
204         if (e.id == Event.MOUSE_DOWN) {
205             int i = cardAt(e.x,e.y);
206
207             if (i != -1) {
208                 setSelected(i,false);
209                 return true;
210             }
211         } else if (e.id == Event.MOUSE_MOVE) {
212             if (applet == null) {
213                 Component c = getParent();
214
215                 while (c != null) {
216                     if (c instanceof Applet)
217                         applet = (Applet) c;
218
219                     c = c.getParent();
220                 }
221             }
222
223         if (applet != null) {
224             int i=cardAt(e.x,e.y);
225
226             if (i != -1)
227                 applet.showStatus(documentCard((String JavaDoc) names.elementAt(i)));
228         }
229     } else if (e.id == Event.MOUSE_EXIT) {
230         if (applet != null)
231             applet.showStatus("");
232     }
233     
234     return super.handleEvent(e);
235 }
236
237   /***************************************************
238     Methods involving size and layout */

239
240   /** Allocates extra margins to give the cards some `body'. */
241     public Insets insets()
242     {
243         return new Insets(tabH+margin,margin,margin,margin);
244     }
245
246   /** Specify the Font to be used for labeling the Tabs.
247     * This avoids getting in the way of cards inheriting default fonts from
248     * the TabPanel's container. */

249     public void setTabFont(Font font)
250     {
251         tabFont = font;
252         metric = getFontMetrics(font);
253         int r = (metric.getHeight()+1)/2;
254         tabH = 2*r;
255
256         // Compute boundaries for the tab edges.
257
int c,s,nn = (tabN-2)/2;
258         double a;
259
260         for(int i=0; i<=nn; i++) {
261             a = Math.PI*i/2/nn;
262             c = (int)(r*Math.cos(a));
263             s = (int)(r*Math.sin(a));
264             tabLeft[0][i] = s;
265             tabLeft[1][i] = r + c;
266             tabLeft[0][i+nn]= tabH -c;
267             tabLeft[1][i+nn]= r - s;
268         }
269
270         tabLeft[0][2*nn+1] = tabH;
271         tabLeft[1][2*nn+1] = tabH;
272
273         for(int i=0; i< tabN; i++) {
274             tabRight[0][i] = -tabLeft[0][i];
275             tabRight[1][i] = tabLeft[1][i];
276         }
277     }
278
279     void computeTabs()
280     { // Compute positions of the tabs.
281
if ((pos == null) || (pos.length <= nCards)) {
282             width = new int[nCards+1];
283             pos = new int[nCards+1];
284         } // make sure pos & width are big enough.
285

286         int x = tabH/2;
287         for(int i=0; i<nCards; i++) { // size the tabs & reshape the panes.
288
pos[i] = x;
289             width[i] = tabH + metric.stringWidth((String JavaDoc) names.elementAt(i));
290             x += width[i];
291         }
292
293         pos[nCards] = x;
294         int w = size().width;
295         if ((offscreen==null) || (offscreen.getHeight(this)<tabH)
296          || (offscreen.getWidth(this)<w)) {
297             offscreen=createImage(w,tabH);
298         }
299     }
300
301   /** Computes tab geometry while laying out the panels components.*/
302     public void layout()
303     {
304         super.layout();
305         computeTabs();
306     } // make sure tabs are computed.
307

308   /***************************************************
309     Painting the tabs */

310
311     void paintTabEdge(Graphics g, int x, int edges[][])
312     {
313         g.translate(x,0);
314         g.setColor(getBackground());
315         g.fillPolygon(edges[0],edges[1],tabN);
316         g.setColor(getForeground());
317
318         for(int i=0; i<tabN-2;i++)
319             g.drawLine(edges[0][i],edges[1][i],edges[0][i+1],edges[1][i+1]);
320
321         g.translate(-x,0);
322     }
323
324     void paintTab(Graphics g, int x, int p)
325     {
326         int r = tabH/2, w = width[p];
327
328         paintTabEdge(g,x-r,tabLeft);
329         paintTabEdge(g,x+w+r,tabRight);
330 // for some reason, this draws in the wrong place on window `repair'!!!
331
// g.clearRect(x+r,0,w-tabH,tabH);
332
g.setColor(getBackground());
333         g.fillRect(x+r,0,w-tabH,tabH);
334         g.setColor(getForeground());
335         g.drawLine(x+r,0,x+w-r,0);
336         g.setFont(tabFont);
337         g.drawString((String JavaDoc) names.elementAt(p),x+r,tabH-metric.getDescent());
338     }
339
340   /** Update (repaint) the TabPanel. Since paint handles the background,
341     * we just call paint directly. */

342     public void update(Graphics g)
343     {
344         paint(g);
345     }
346
347   /** Paint the tabs in a row atop the cards. */
348     public void paint(Graphics gg)
349     {
350         Dimension sz = size();
351         Graphics g = offscreen.getGraphics();
352         int x,w = sz.width-1, h = sz.height-1, r = tabH/2;
353         int j, s = selected;
354         // These come into play when the tabs span more than the panel width.
355
// Show some `shadow' tabs at the ends to represent those not displayed.
356
int shadow = 4; // how wide of a shadow (pixels) to show
357
int nShadows = 3; // max # of shadow tabs to show.
358
Color backback= getParent().getBackground();
359
360         // Fill in tab area in the PARENT's color
361
g.setColor(getParent().getBackground());
362         g.fillRect(0,0,w+1,tabH);
363         g.setColor(getForeground());
364
365         if (nCards == 0)
366             g.drawLine(0,tabH,w,tabH);
367         else {
368             // Possibly adjust the offset, so at least the selected tab is visible
369
int offmax = pos[s] - r - Math.min(nShadows,s)*shadow,
370              offmin = pos[s+1] - w + r + Math.min(nCards-s,nShadows)*shadow;
371
372             if ((offset < offmin) || (offset > offmax))
373                 offset = Math.min(Math.max(0,(offmin+offmax)/2),pos[nCards]+r-w);
374
375             // Draw first tabs from the left (offscreen ones only partly visible)
376
for(j = 0, x = offset+r;(j < s) && (pos[j] <= x); j++); // find visible
377

378             if (j > 0) {
379                 x = 0;
380
381                 for(int i=Math.max(0,j-nShadows); i<j-1; i++, x+=shadow)
382                     paintTabEdge(g,x,tabLeft);
383
384                 paintTab(g,x+r,j-1);
385             }
386
387             for(int i = j; i < s; i++) {
388                 paintTab(g,pos[i]-offset,i);
389             }
390
391             // Draw last tabs from the right (offscreen ones only partly visible)
392
for(j = nCards-1, x = offset+w-r; (j > s) && (pos[j+1] >= x); j--);
393
394             if (j < nCards-1) {
395                 x = w;
396
397                 for(int i=Math.min(nCards-1,j+nShadows); i>j+1; i--, x-=shadow)
398                     paintTabEdge(g,x,tabRight);
399
400                 paintTab(g,x-r-width[j+1],j+1);
401             }
402             
403             for(int i = j; i > s; i--) {
404                 paintTab(g,pos[i]-offset,i);
405             }
406       
407             // now draw the selected tab on top of the others.
408
g.clearRect(pos[s]-r-offset+2,tabH-1,width[s]+tabH-1,1);
409             paintTab(g,pos[s]-offset,s);
410             // and fixup the baseline so the selected is on `top'.
411
g.drawLine(0,tabH-1,pos[s]-r-offset+1,tabH-1);
412             g.drawLine(pos[s+1]+r-offset-1,tabH-1,w,tabH-1);
413         }
414
415         gg.drawImage(offscreen,0,0,this);
416         gg.drawLine(w,tabH,w,h);
417         gg.drawLine(w,h,0,h);
418         gg.drawLine(0,h,0,tabH);
419     }
420 }
421
422
Popular Tags