KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jgoodies > looks > windows > WindowsTabbedPaneUI


1 /*
2  * Copyright (c) 2001-2005 JGoodies Karsten Lentzsch. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * o Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  *
10  * o Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * o Neither the name of JGoodies Karsten Lentzsch nor the names of
15  * its contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31 package com.jgoodies.looks.windows;
32
33 import java.awt.FontMetrics JavaDoc;
34 import java.awt.Graphics JavaDoc;
35 import java.awt.Insets JavaDoc;
36 import java.awt.Rectangle JavaDoc;
37 import java.beans.PropertyChangeEvent JavaDoc;
38 import java.beans.PropertyChangeListener JavaDoc;
39
40 import javax.swing.Icon JavaDoc;
41 import javax.swing.JComponent JavaDoc;
42 import javax.swing.SwingConstants JavaDoc;
43 import javax.swing.SwingUtilities JavaDoc;
44 import javax.swing.plaf.ComponentUI JavaDoc;
45 import javax.swing.plaf.basic.BasicGraphicsUtils JavaDoc;
46 import javax.swing.plaf.basic.BasicTabbedPaneUI JavaDoc;
47 import javax.swing.text.View JavaDoc;
48
49 import com.jgoodies.looks.Options;
50
51 /**
52  * The JGoodies Windows L&amp;F implementation of <code>TabbedPaneUI</code>.<p>
53  *
54  * The flat appearance is work in progress; currently it works only
55  * for a single line of tabs and paints distored tabs for multiple lines.
56  *
57  * @author Karsten Lentzsch
58  * @version $Revision: 1.4 $
59  */

60 public final class WindowsTabbedPaneUI extends com.sun.java.swing.plaf.windows.WindowsTabbedPaneUI {
61
62     private static final Insets JavaDoc EMPTY_INSETS = new Insets JavaDoc(0, 0, 0, 0);
63     private static final Insets JavaDoc NORTH_INSETS = new Insets JavaDoc(1, 0, 0, 0);
64     private static final Insets JavaDoc WEST_INSETS = new Insets JavaDoc(0, 1, 0, 0);
65     private static final Insets JavaDoc SOUTH_INSETS = new Insets JavaDoc(0, 0, 1, 0);
66     private static final Insets JavaDoc EAST_INSETS = new Insets JavaDoc(0, 0, 0, 1);
67
68
69     /**
70      * Describes if tabs are painted with or without icons.
71      */

72     private static boolean isTabIconsEnabled = Options.isTabIconsEnabled();
73
74     /**
75      * Describes if we paint no content border or not; is false by default.
76      * You can disable the content border by setting the client property
77      * Options.NO_CONTENT_BORDER_KEY to Boolean.TRUE;
78      * <p>
79      * Overrides any ClearLook considerations.
80      */

81     private Boolean JavaDoc noContentBorder;
82
83     /**
84      * Describes if we paint tabs in an embedded style that is with
85      * less decoration; this is false by default.
86      * You can enable the embedded tabs style by setting the client property
87      * Options.EMBEDDED_TABS_KEY to Boolean.TRUE.
88      * <p>
89      * Overrides any ClearLook considerations.
90      */

91     private Boolean JavaDoc embeddedTabs;
92
93     /**
94      * Creates and answers the <code>WindowsTabbedPaneUI</code>.
95      *
96      * @see javax.swing.plaf.ComponentUI#createUI(JComponent)
97      */

98     public static ComponentUI JavaDoc createUI(JComponent JavaDoc x) {
99         return new WindowsTabbedPaneUI();
100     }
101
102
103     /**
104      * Installs the UI.
105      *
106      * @see javax.swing.plaf.ComponentUI#installUI(JComponent)
107      */

108     public void installUI(JComponent JavaDoc c) {
109         super.installUI(c);
110         embeddedTabs = (Boolean JavaDoc) c.getClientProperty(Options.EMBEDDED_TABS_KEY);
111         noContentBorder = (Boolean JavaDoc) c.getClientProperty(Options.NO_CONTENT_BORDER_KEY);
112     }
113
114
115     /**
116      * Checks and answers if content border will be painted.
117      * This is controlled by the component's client property
118      * Options.NO_CONTENT_BORDER or Options.EMBEDDED.
119      */

120     private boolean hasNoContentBorder() {
121         return hasEmbeddedTabs() || Boolean.TRUE.equals(noContentBorder);
122     }
123
124     /**
125      * Checks and answers if tabs are painted with minimal decoration.
126      */

127     private boolean hasEmbeddedTabs() {
128         return embeddedTabs == null
129                 ? false
130                 : embeddedTabs.booleanValue();
131     }
132
133     /**
134      * Creates and answer a handler that listens to property changes.
135      * Unlike the superclass BasicTabbedPane, the PlasticTabbedPaneUI
136      * uses an extended Handler.
137      */

138     protected PropertyChangeListener JavaDoc createPropertyChangeListener() {
139         return new MyPropertyChangeHandler();
140     }
141
142     private void doLayout() {
143         tabPane.revalidate();
144         tabPane.repaint();
145     }
146     
147     /**
148      * Updates the embedded tabs property. This message is sent by
149      * my PropertyChangeHandler whenever the embedded tabs property changes.
150      */

151     private void embeddedTabsPropertyChanged(Boolean JavaDoc newValue) {
152         embeddedTabs = newValue;
153         doLayout();
154     }
155
156      /**
157       * Updates the no content border property. This message is sent
158       * by my PropertyChangeHandler whenever the noContentBorder
159       * property changes.
160       */

161      private void noContentBorderPropertyChanged(Boolean JavaDoc newValue) {
162          noContentBorder = newValue;
163          doLayout();
164      }
165
166
167
168     /**
169      * Answers the icon for the tab with the specified index.
170      * In case, we have globally switched of the use tab icons,
171      * we answer <code>null</code> if and only if we have a title.
172      */

173     protected Icon JavaDoc getIconForTab(int tabIndex) {
174         String JavaDoc title = tabPane.getTitleAt(tabIndex);
175         boolean hasTitle = (title != null) && (title.length() > 0);
176         return !isTabIconsEnabled && hasTitle
177                     ? null
178                     : super.getIconForTab(tabIndex);
179     }
180
181     protected Insets JavaDoc getContentBorderInsets(int tabPlacement) {
182         if (!hasNoContentBorder())
183             return contentBorderInsets;
184         else if (hasEmbeddedTabs())
185             return EMPTY_INSETS;
186         else {
187             switch (tabPlacement) {
188                 case RIGHT :
189                     return EAST_INSETS;
190                 case LEFT :
191                     return WEST_INSETS;
192                 case TOP :
193                     return NORTH_INSETS;
194                 case BOTTOM :
195                 default :
196                     return SOUTH_INSETS;
197             }
198         }
199     }
200     
201     protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
202         switch (tabPlacement) {
203             case RIGHT :
204                 return isSelected ? 2 : 0;
205             case LEFT :
206                 return isSelected ? -2 : 0;
207             case TOP :
208             case BOTTOM :
209             default :
210                 return 0;
211         }
212     }
213
214     protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
215         return 0;
216     }
217
218     protected Insets JavaDoc getSelectedTabPadInsets(int tabPlacement) {
219         if (hasEmbeddedTabs()) {
220             return EMPTY_INSETS;
221         } else if (hasNoContentBorder()) {
222             switch (tabPlacement) {
223                 case LEFT: return new Insets JavaDoc(1,2,1,0);
224                 case RIGHT: return new Insets JavaDoc(1,0,1,2);
225                 case TOP: return new Insets JavaDoc(2,2,0,2);
226                 case BOTTOM: return new Insets JavaDoc(0,2,2,2);
227                 default: return EMPTY_INSETS;
228             }
229         } else {
230             Insets JavaDoc superInsets = super.getSelectedTabPadInsets(tabPlacement);
231             int equalized = superInsets.left + superInsets.right / 2;
232             superInsets.left = superInsets.right = equalized;
233             return superInsets;
234         }
235     }
236
237
238     protected Insets JavaDoc getTabAreaInsets(int tabPlacement) {
239         return hasEmbeddedTabs()
240                 ? /*new Insets(1,1,1,1)*/EMPTY_INSETS
241                 : super.getTabAreaInsets(tabPlacement);
242     }
243     
244     /**
245      * Paints the top edge of the pane's content border
246      */

247     protected void paintContentBorderTopEdge(Graphics JavaDoc g, int tabPlacement,
248                                          int selectedIndex,
249                                          int x, int y, int w, int h) {
250         if (hasNoContentBorder() && tabPlacement != TOP) {
251             return;
252         }
253         Rectangle JavaDoc selRect = selectedIndex < 0
254                             ? null
255                             : getTabBounds(selectedIndex, calcRect);
256         if (tabPlacement != TOP || selectedIndex < 0 ||
257            (selRect.y + selRect.height + 1 < y) ||
258            (selRect.x < x || selRect.x > x + w)) {
259             // no special case, do the super thing
260
super.paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
261         } else {
262             g.setColor(lightHighlight);
263             g.fillRect(x, y, selRect.x + 1-x, 1);
264             g.fillRect(selRect.x + selRect.width, y,
265                        x+w-2 -selRect.x-selRect.width, 1);
266         }
267     }
268     
269     /**
270      * Paints the bottom edge of the pane's content border.
271      */

272     protected void paintContentBorderBottomEdge(Graphics JavaDoc g, int tabPlacement,
273                                          int selectedIndex,
274                                          int x, int y, int w, int h) {
275         if (!hasNoContentBorder()) {
276             Rectangle JavaDoc selRect = selectedIndex < 0? null :
277                            getTabBounds(selectedIndex, calcRect);
278             if (tabPlacement != BOTTOM || selectedIndex < 0 ||
279              (selRect.y - 1 > h + y) ||
280          (selRect.x < x || selRect.x > x + w)) {
281            // no special case, do the super thing
282
super.paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
283          } else {
284             g.setColor(lightHighlight);
285             g.fillRect(x,y+h-1,1,1);
286             g.setColor(shadow);
287             g.fillRect(x+1, y+h-2, selRect.x - 1-x, 1);
288             g.fillRect(selRect.x + selRect.width, y+h-2, x+w-2-selRect.x-selRect.width, 1);
289             g.setColor(darkShadow);
290             g.fillRect(x, y+h-1, selRect.x - x, 1);
291             g.fillRect(selRect.x + selRect.width -1, y+h-1, x+w-selRect.x-selRect.width, 1);
292          }
293         } else if (!(tabPlacement == BOTTOM)) {
294             // no content border really means only one content border:
295
// the one edge that touches the tabs
296
} else {
297             g.setColor(shadow);
298             g.fillRect(x,y+h,w,1);
299         }
300     }
301
302     /**
303      * paints the left Edge of the pane's content border
304      */

305     protected void paintContentBorderLeftEdge(Graphics JavaDoc g, int tabPlacement,
306                                          int selectedIndex,
307                                          int x, int y, int w, int h) {
308         if (!hasNoContentBorder()) {
309             Rectangle JavaDoc selRect = selectedIndex < 0? null :
310                            getTabBounds(selectedIndex, calcRect);
311             if (tabPlacement != LEFT || selectedIndex < 0 ||
312                 (selRect.x + selRect.width + 1 < x) ||
313                 (selRect.y < y || selRect.y > y + h)) {
314                  // no special case, do the super thing
315
super.paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
316             } else {
317                g.setColor(lightHighlight);
318                g.fillRect(x, y, 1, selRect.y + 1 - y);
319                g.fillRect(x, selRect.y + selRect.height,
320                            1, y+h-1-selRect.y-selRect.height);
321                 
322             }
323         } else if (!(tabPlacement == LEFT)) {
324             // no content border really means only one content border:
325
// the one edge that touches the tabs
326
} else {
327             g.setColor(shadow);
328             g.fillRect(x,y,1,h);
329         }
330     }
331
332     /**
333      * paints the right Edge of the pane's content border
334      */

335     protected void paintContentBorderRightEdge(Graphics JavaDoc g, int tabPlacement,
336                                          int selectedIndex,
337                                          int x, int y, int w, int h) {
338         if (!hasNoContentBorder()) {
339             Rectangle JavaDoc selRect = selectedIndex < 0? null :
340                            getTabBounds(selectedIndex, calcRect);
341             if (tabPlacement != RIGHT || selectedIndex < 0 ||
342                (selRect.x - 1 > x+w) ||
343                (selRect.y < y || selRect.y > y + h)) {
344                 // no special case, do the super thing
345
super.paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
346              } else {
347                g.setColor(lightHighlight);
348                g.fillRect(x+w-1, y,1,1);
349                g.setColor(shadow);
350                g.fillRect(x+w-2, y+1, 1, selRect.y - 1-y);
351                g.fillRect(x+w-2, selRect.y + selRect.height,
352                            1, y+h-1-selRect.y- selRect.height);
353                g.setColor(darkShadow);
354                g.fillRect(x+w-1, y, 1, selRect.y - y);
355                g.fillRect(x+w-1, selRect.y + selRect.height-1,
356                            1, y+h-selRect.y-selRect.height);
357           
358              }
359         } else if (!(tabPlacement == RIGHT)) {
360             // no content border really means only one content border:
361
// the one edge that touches the tabs
362
} else {
363             g.setColor(shadow);
364             g.fillRect(x+w,y,1,h);
365         }
366     }
367
368
369    /**
370      * Paints the border for a single tab; it does not paint the tab's background.
371      */

372     protected void paintTabBorder(
373         Graphics JavaDoc g,
374         int tabPlacement,
375         int tabIndex,
376         int x,
377         int y,
378         int w,
379         int h,
380         boolean isSelected) {
381         if (!hasEmbeddedTabs()) {
382             super.paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
383             return;
384         }
385         g.translate(x - 1, y - 1);
386         int w1, w2, w3;
387         int h1, h2, h3;
388         switch (tabPlacement) {
389             case TOP :
390                 w1 = 1;
391                 w2 = w - 2;
392                 w3 = 1;
393                 h1 = 1;
394                 h2 = h - 1;
395                 h3 = 0;
396                 break;
397             case BOTTOM :
398                 w1 = 1;
399                 w2 = w - 2;
400                 w3 = 1;
401                 h1 = 0;
402                 h2 = h - 1;
403                 h3 = 1;
404                 break;
405             case LEFT :
406                 w1 = 1;
407                 w2 = w - 1;
408                 w3 = 0;
409                 h1 = 1;
410                 h2 = h - 3;
411                 h3 = 1;
412                 break;
413             case RIGHT :
414             default :
415                 w1 = 0;
416                 w2 = w - 1;
417                 w3 = 1;
418                 h1 = 1;
419                 h2 = h - 3;
420                 h3 = 1;
421         }
422         if (isSelected) {
423             g.setColor(lightHighlight);
424             g.drawRect(w1, h1, w1 + w2 + w3, h1 + h2 + h3);
425             g.setColor(shadow);
426             g.fillRect(1 + w1, 0, w2, h1);
427             g.fillRect(0, 1 + h1, w1, h2);
428             g.fillRect(2 * w1 + w2 + 2 * w3, 1 + h1, w3, h2);
429             g.fillRect(1 + w1, 2 * h1 + h2 + 2 * h3, w2, h3);
430             g.fillRect(1, 1, w1, h1);
431             g.fillRect(2 * w1 + w2 + w3, 1, w3, h1);
432             g.fillRect(1, 2 * h1 + h2 + h3, w1, h3);
433             g.fillRect(2 * w1 + w2 + w3, 2 * h1 + h2 + h3, w3, h3);
434         } else {
435             g.setColor(shadow);
436             g.fillRect(w1 + w2 + 2 * w3, h3 * h2 /2, w3, h2* 2 /3);
437             g.fillRect(w3*w2 /2, h1 + h2 + 2 * h3, w2/2 +2, h3);
438         }
439         g.translate(-x + 1, -y + 1);
440     }
441
442     protected void paintFocusIndicator(
443         Graphics JavaDoc g,
444         int tabPlacement,
445         Rectangle JavaDoc[] rectangles,
446         int tabIndex,
447         Rectangle JavaDoc iconRect,
448         Rectangle JavaDoc textRect,
449         boolean isSelected) {
450         if (!hasEmbeddedTabs()) {
451             super.paintFocusIndicator(g, tabPlacement, rectangles, tabIndex, iconRect, textRect, isSelected);
452             return;
453         }
454         if (tabPane.hasFocus() && isSelected) {
455             g.setColor(focus);
456             BasicGraphicsUtils.drawDashedRect(g, textRect.x - 2, textRect.y, textRect.width + 3, textRect.height);
457         }
458     }
459
460     protected boolean shouldRotateTabRuns(int tabPlacement) {
461         return !hasEmbeddedTabs();
462     }
463
464     /**
465      * Copied here from super(super)class to avoid labels being centered on
466      * vertical tab runs if they consist of icon and text
467      */

468     protected void layoutLabel(
469         int tabPlacement,
470         FontMetrics JavaDoc metrics,
471         int tabIndex,
472         String JavaDoc title,
473         Icon JavaDoc icon,
474         Rectangle JavaDoc tabRect,
475         Rectangle JavaDoc iconRect,
476         Rectangle JavaDoc textRect,
477         boolean isSelected) {
478         textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
479         
480         //fix of issue #4
481
View JavaDoc v = getTextViewForTab(tabIndex);
482         if (v != null) {
483             tabPane.putClientProperty("html", v);
484         }
485         
486         int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
487         int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
488         if ((tabPlacement == RIGHT || tabPlacement == LEFT) && icon != null && title != null && !title.equals("")) {
489             /* vertical tab runs look ugly if icons and text are centered */
490             SwingUtilities.layoutCompoundLabel(
491                 tabPane,
492                 metrics,
493                 title,
494                 icon,
495                 SwingConstants.CENTER,
496                 SwingConstants.LEFT,
497                 SwingConstants.CENTER,
498                 SwingConstants.TRAILING,
499                 tabRect,
500                 iconRect,
501                 textRect,
502                 textIconGap);
503             xNudge += 4;
504         } else { /* original superclass behavior */
505             SwingUtilities.layoutCompoundLabel(
506                 tabPane,
507                 metrics,
508                 title,
509                 icon,
510                 SwingConstants.CENTER,
511                 SwingConstants.CENTER,
512                 SwingConstants.CENTER,
513                 SwingConstants.TRAILING,
514                 tabRect,
515                 iconRect,
516                 textRect,
517                 textIconGap);
518         }
519
520         //fix of issue #4
521
tabPane.putClientProperty("html", null);
522         
523         iconRect.x += xNudge;
524         iconRect.y += yNudge;
525         textRect.x += xNudge;
526         textRect.y += yNudge;
527     }
528     
529     
530     /**
531      * Catches and handles property change events. In addition to the super
532      * class behavior we listen to changes of the ancestor, tab placement,
533      * and JGoodies options for content border, and embedded tabs.
534      */

535     private class MyPropertyChangeHandler extends BasicTabbedPaneUI.PropertyChangeHandler JavaDoc {
536         public void propertyChange(PropertyChangeEvent JavaDoc e) {
537             super.propertyChange(e);
538
539             String JavaDoc pName = e.getPropertyName();
540             if (null == pName) {
541                 return;
542             }
543             if (pName.equals(Options.EMBEDDED_TABS_KEY)) {
544                embeddedTabsPropertyChanged((Boolean JavaDoc)e.getNewValue());
545                return;
546             }
547             if (pName.equals(Options.NO_CONTENT_BORDER_KEY)) {
548                noContentBorderPropertyChanged((Boolean JavaDoc)e.getNewValue());
549                return;
550             }
551
552         }
553
554     }
555
556     
557
558 }
Popular Tags