KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > swing > tabcontrol > plaf > BasicSlidingTabDisplayerUI


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18 /*
19  * BasicSlidingTabDisplayerUI.java
20  *
21  * Created on March 27, 2004, 7:14 AM
22  */

23
24 package org.netbeans.swing.tabcontrol.plaf;
25
26 import org.netbeans.swing.tabcontrol.TabDisplayer;
27
28 import javax.swing.*;
29 import javax.swing.event.ChangeEvent JavaDoc;
30 import javax.swing.event.ChangeListener JavaDoc;
31 import javax.swing.plaf.ButtonUI JavaDoc;
32 import javax.swing.plaf.ComponentUI JavaDoc;
33 import java.awt.*;
34 import java.awt.event.*;
35 import java.awt.image.BufferedImage JavaDoc;
36 import java.beans.PropertyChangeEvent JavaDoc;
37 import java.util.Arrays JavaDoc;
38 import java.util.Comparator JavaDoc;
39 import org.netbeans.swing.tabcontrol.TabData;
40
41 /** Common UI for sliding tabs. Simply uses JToggleButtons for displayers,
42  * since the contents of the data model are not expected to change often,
43  * and no scrolling behavior needs to be supported for the tabs area.
44  * <p>
45  * Note that the &quot;sliding&quot; is provided by an instance of <code>FxProvider</code>
46  * provided in the <code>DefaultTabbedContainerUI</code>, not here.
47  * <p>
48  * To change the appearance of the buttons, simply provide a subclass of <code>SlidingTabDisplayerButtonUI</code>
49  * via UIDefaults. This class is final.
50  *
51  * @author Tim Boudreau
52  */

53 public final class BasicSlidingTabDisplayerUI extends AbstractTabDisplayerUI {
54     private Rectangle scratch = new Rectangle();
55     
56     /** Creates a new instance of BasicSlidingTabDisplayerUI */
57     public BasicSlidingTabDisplayerUI(TabDisplayer displayer) {
58         super (displayer);
59     }
60     
61     public static ComponentUI JavaDoc createUI (JComponent c) {
62         return new BasicSlidingTabDisplayerUI((TabDisplayer) c);
63     }
64     
65     protected void install() {
66         displayer.setLayout (new OrientedLayoutManager());
67         syncButtonsWithModel();
68     }
69     
70     protected Font createFont() {
71         //XXX Sideways text is more readable with a slightly larger bold font
72
Font f = super.createFont();
73                 // don't use deriveFont() - see #49973 for details
74
f = new Font(f.getName(), Font.BOLD, f.getSize() + 1);
75         return f;
76     }
77
78     protected void uninstall() {
79         displayer.removeAll();
80     }
81     
82     public Dimension getPreferredSize(JComponent c) {
83         return displayer.getLayout().preferredLayoutSize(c);
84     }
85
86     public Dimension getMinimumSize(JComponent c) {
87         return displayer.getLayout().minimumLayoutSize(c);
88     }
89
90     private int buttonCount = 0;
91     private boolean syncButtonsWithModel() {
92         assert SwingUtilities.isEventDispatchThread();
93         
94         int count = displayer.getModel().size();
95         boolean changed = false;
96         buttonCount = displayer.getComponentCount();
97
98         if (count != buttonCount) {
99             synchronized (displayer.getTreeLock()) {
100                 while (count < buttonCount) {
101                     if (buttonCount-- > 0) {
102                         displayer.remove (buttonCount- 1);
103                         changed = true;
104                     }
105                 }
106                 while (count > buttonCount) {
107                     IndexButton ib = new IndexButton (buttonCount++);
108                     ib.setFont (displayer.getFont());
109                     displayer.add (ib);
110                     changed = true;
111                 }
112                 Component[] c = displayer.getComponents();
113                 for (int i=0; i < c.length; i++) {
114                     if (c[i] instanceof IndexButton) {
115                         changed |= ((IndexButton) c[i]).checkChanged();
116                     }
117                 }
118             }
119         }
120         return changed;
121     }
122     
123     /** Not used so much to determine layout as to calculate preferred sizes
124      * here
125      */

126     protected TabLayoutModel createLayoutModel() {
127        DefaultTabLayoutModel result = new DefaultTabLayoutModel(displayer.getModel(), displayer);
128        result.setPadding (new Dimension (15, 2));
129        return result;
130     }
131     
132     protected MouseListener createMouseListener() {
133         return new MouseAdapter() {};
134     }
135     
136     public void requestAttention (int tab) {
137         //not implemented
138
}
139     
140     public void cancelRequestAttention (int tab) {
141         //not implemented
142
}
143     
144     protected ChangeListener JavaDoc createSelectionListener() {
145         return new ChangeListener JavaDoc() {
146             private int lastKnownSelection = -1;
147             public void stateChanged (ChangeEvent JavaDoc ce) {
148                 int selection = selectionModel.getSelectedIndex();
149                 if (selection != lastKnownSelection) {
150                     if (lastKnownSelection != -1) {
151                         IndexButton last = findButtonFor(lastKnownSelection);
152                         if (last != null) {
153                             last.getModel().setSelected(false);
154                         }
155                     }
156                     if (selection != -1) {
157                         IndexButton current = findButtonFor (selection);
158                         if (displayer.getComponentCount() == 0) {
159                             syncButtonsWithModel();
160                         }
161                         if (current != null) {
162                             current.getModel().setSelected (true);
163                         }
164                     }
165                 }
166                 lastKnownSelection = selection;
167             }
168         };
169     }
170     
171     public Polygon getExactTabIndication(int index) {
172         return new EqualPolygon (findButtonFor(index).getBounds());
173     }
174     
175     public Polygon getInsertTabIndication(int index) {
176         Rectangle r = findButtonFor (index).getBounds();
177         Polygon result = new EqualPolygon (findButtonFor(index).getBounds());
178         return result;
179     }
180     
181     private IndexButton findButtonFor (int index) {
182         Component[] c = displayer.getComponents();
183         for (int i=0; i < c.length; i++) {
184             if (c[i] instanceof IndexButton && ((IndexButton) c[i]).getIndex() == index) {
185                 return (IndexButton) c[i];
186             }
187         }
188         return null;
189     }
190     
191     public Rectangle getTabRect(int index, Rectangle destination) {
192         if (destination == null) {
193             destination = new Rectangle();
194         }
195         IndexButton ib = findButtonFor(index);
196         if (ib != null) {
197             destination.setBounds (ib.getBounds());
198         } else {
199             destination.setBounds (-20, -20, 0, 0);
200         }
201         return destination;
202     }
203     
204     public int tabForCoordinate(Point p) {
205         Component[] c = displayer.getComponents();
206         for (int i=0; i < c.length; i++) {
207             if (c[i] instanceof IndexButton) {
208                 if (c[i].contains(p)) {
209                     return ((IndexButton) c[i]).getIndex();
210                 }
211             }
212         }
213         return -1;
214     }
215
216     protected final class SlidingPropertyChangeListener extends AbstractTabDisplayerUI.DisplayerPropertyChangeListener {
217         public void propertyChange(PropertyChangeEvent JavaDoc e) {
218             super.propertyChange(e);
219             if (TabDisplayer.PROP_ORIENTATION.equals(e.getPropertyName())) {
220                 displayer.revalidate();
221             }
222         }
223     }
224     
225     private Object JavaDoc getDisplayerOrientation() {
226         return displayer.getClientProperty (TabDisplayer.PROP_ORIENTATION);
227     }
228
229      /** Paints the rectangle occupied by a tab into an image and returns the result */
230     public Image JavaDoc createImageOfTab(int index) {
231         TabData td = displayer.getModel().getTab(index);
232         
233         JLabel lbl = new JLabel(td.getText());
234         int width = lbl.getFontMetrics(lbl.getFont()).stringWidth(td.getText());
235         int height = lbl.getFontMetrics(lbl.getFont()).getHeight();
236         width = width + td.getIcon().getIconWidth() + 6;
237         height = Math.max(height, td.getIcon().getIconHeight()) + 5;
238         
239         GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment()
240                                         .getDefaultScreenDevice().getDefaultConfiguration();
241         
242         BufferedImage JavaDoc image = config.createCompatibleImage(width, height);
243         Graphics2D g = image.createGraphics();
244         g.setColor(lbl.getForeground());
245         g.setFont(lbl.getFont());
246         td.getIcon().paintIcon(lbl, g, 0, 0);
247         g.drawString(td.getText(), 18, height / 2);
248         
249         return image;
250
251     }
252
253     /**
254      * JToggleButton subclass which maps to an index in the data model, and displays
255      * whatever the content of the data model at that index is. Buttons are added or removed
256      * from the tab displayer as the model changes. This class is public to allow
257      * alternate UIs for the buttons to be provided via subclasses of <code>SlidingTabDisplayerButtonUI</code>.
258      */

259     public final class IndexButton extends JToggleButton implements ActionListener {
260         private int index;
261         private String JavaDoc lastKnownText = null;
262         private Icon lastKnownIcon = null;
263
264         /** UI Class ID for IndexButtons, to be used by providers of UI delegates */
265         public static final String JavaDoc UI_KEY = "IndexButtonUI"; //NOI18N
266

267         /** Create a new button representing an index in the model. The index is immutable for the life of the
268          * button.
269          *
270          * @param index The index
271          */

272         public IndexButton (int index) {
273             this.index = index;
274             addActionListener(this);
275             setFont (displayer.getFont());
276             setFocusable(false);
277         }
278
279         public void addNotify() {
280             super.addNotify();
281             ToolTipManager.sharedInstance().registerComponent(this);
282         }
283
284         public void removeNotify() {
285             super.removeNotify();
286             ToolTipManager.sharedInstance().unregisterComponent(this);
287         }
288
289         /** Accessor for the UI delegate to determine if the tab displayer is currently active */
290         public boolean isActive() {
291             return displayer.isActive();
292         }
293
294         public void updateUI () {
295             SlidingTabDisplayerButtonUI ui = null;
296             try {
297                 ui = (SlidingTabDisplayerButtonUI) UIManager.getUI(this);
298                 setUI (ui);
299                 return;
300             } catch (Error JavaDoc e) {
301                 System.err.println ("Error getting sliding button UI: " + e.getMessage());
302             } catch (Exception JavaDoc ex) {
303                 System.err.println ("Exception getting button UI: " + ex.getMessage());
304             }
305             setUI ((ButtonUI JavaDoc) SlidingTabDisplayerButtonUI.createUI(this));
306         }
307
308         public String JavaDoc getUIClassID() {
309             return UI_KEY;
310         }
311
312         /** Accessor for the UI delegate - orientation will be one of the constants defined on
313          * TabDisplayer */

314         public Object JavaDoc getOrientation() {
315             return getDisplayerOrientation();
316         }
317         
318         public String JavaDoc getText() {
319             if (index == -1) {
320                 //We're being called in the superclass constructor when the UI is
321
//assigned
322
return "";
323             }
324             if (index < displayer.getModel().size()) {
325                 lastKnownText = displayer.getModel().getTab(index).getText();
326             } else {
327                 return "This tab doesn't exist."; //NOI18N
328
}
329             return lastKnownText;
330         }
331         
332         public String JavaDoc getToolTipText() {
333             return displayer.getModel().getTab(index).getTooltip();
334         }
335
336         /** Implementation of ActionListener - sets the selected index in the selection model */
337         public final void actionPerformed(ActionEvent e) {
338             if (!isSelected()) {
339                 selectionModel.setSelectedIndex (-1);
340             } else {
341                 selectionModel.setSelectedIndex (index);
342             }
343         }
344
345         /** Get the index into the data model that this button represents */
346         public int getIndex() {
347             return index;
348         }
349         
350         public Icon getIcon() {
351             if (index == -1) {
352                 //We're being called in the superclass constructor when the UI is
353
//assigned
354
return null;
355             }
356             if (index < displayer.getModel().size()) {
357                 lastKnownIcon = displayer.getModel().getTab(index).getIcon();
358             }
359             return lastKnownIcon;
360         }
361
362         /**
363          * Test if the text or icon in the model has changed since the last time <code>getText()</code> or
364          * <code>getIcon()</code> was called. If a change has occured, the button will fire the appropriate
365          * property changes, including preferred size, to ensure the tab displayer is re-laid out correctly.
366          * This method is called when a change happens in the model over the index this button represents.
367          *
368          * @return true if something has changed
369          */

370         final boolean checkChanged() {
371             boolean result = false;
372             Icon ic = lastKnownIcon;
373             Icon nue = getIcon();
374             if (nue != ic) {
375                 firePropertyChange ("icon", lastKnownIcon, nue); //NOI18N
376
result = true;
377             }
378             String JavaDoc txt = lastKnownText;
379             String JavaDoc nu = getText();
380             if (nu != txt) { //Equality compare probably not needed
381
firePropertyChange ("text", lastKnownText, getText()); //NOI18N
382
result = true;
383             }
384             if (result) {
385                 firePropertyChange ("preferredSize", null, null); //NOI18N
386
}
387             return result;
388         }
389     }
390
391     protected void modelChanged() {
392         if (syncButtonsWithModel()) {
393             displayer.validate();
394         }
395     }
396
397     public Icon getButtonIcon(int buttonId, int buttonState) {
398         return null;
399     }
400
401     private static final Comparator JavaDoc<Component> BUTTON_COMPARATOR = new IndexButtonComparator();
402     private static class IndexButtonComparator implements Comparator JavaDoc<Component> {
403         public int compare(Component o1, Component o2) {
404             if (o2 instanceof IndexButton && o1 instanceof IndexButton) {
405                 return ((IndexButton) o1).getIndex() - ((IndexButton) o2).getIndex();
406             }
407             return 0;
408         }
409     }
410     
411     private final class OrientedLayoutManager implements LayoutManager {
412         public void addLayoutComponent(String JavaDoc name, Component comp) {
413             //do nothing
414
}
415         
416         public void layoutContainer(Container parent) {
417             synchronized (parent.getTreeLock()) {
418                 syncButtonsWithModel();
419                 Component[] c = parent.getComponents();
420                 Arrays.sort (c, BUTTON_COMPARATOR);
421                 for (int i=0; i < c.length; i++) {
422                     if (c[i] instanceof IndexButton) {
423                         boundsFor ((IndexButton) c[i], scratch);
424                         c[i].setBounds (scratch);
425                     }
426                 }
427             }
428         }
429         
430         private void boundsFor (IndexButton b, Rectangle r) {
431             Object JavaDoc orientation = getDisplayerOrientation();
432             boolean flip = orientation == TabDisplayer.ORIENTATION_EAST ||
433                 orientation == TabDisplayer.ORIENTATION_WEST;
434             int index = b.getIndex();
435             
436             if (index >= displayer.getModel().size() || index < 0) {
437                 r.setBounds (-20, -20, 0, 0);
438                 return;
439             }
440
441             r.x = layoutModel.getX(index);
442             r.y = layoutModel.getY(index);
443             r.width = layoutModel.getW(index);
444             r.height = layoutModel.getH(index);
445             if (flip) {
446                 int tmp = r.x;
447                 r.x = r.y;
448                 r.y = tmp;
449                 
450                 tmp = r.width;
451                 r.width = r.height;
452                 r.height = tmp;
453             }
454         }
455         
456         public Dimension minimumLayoutSize(Container parent) {
457             return preferredLayoutSize(parent);
458         }
459         
460         public Dimension preferredLayoutSize(Container parent) {
461             Object JavaDoc orientation = getDisplayerOrientation();
462             boolean flip = orientation == TabDisplayer.ORIENTATION_EAST ||
463                 orientation == TabDisplayer.ORIENTATION_WEST;
464             
465             int max = displayer.getModel().size();
466             Dimension result = new Dimension();
467             for (int i=0; i < max; i++) {
468                 result.height = Math.max (result.height, layoutModel.getH(i));
469                 result.width += layoutModel.getW(i);
470             }
471             if (flip) {
472                 int tmp = result.height;
473                 result.height = result.width;
474                 result.width = tmp;
475             }
476             return result;
477         }
478         
479         public void removeLayoutComponent(Component comp) {
480             //do nothing
481
}
482     }
483 }
484
Popular Tags