1 /* 2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.java.swing.plaf.windows; 27 28 import java.awt.*; 29 30 import javax.swing.plaf.basic.*; 31 import javax.swing.plaf.*; 32 import javax.swing.*; 33 import java.util.Set; 34 import java.util.HashSet; 35 import java.awt.event.*; 36 37 import static com.sun.java.swing.plaf.windows.TMSchema.*; 38 import static com.sun.java.swing.plaf.windows.XPStyle.Skin; 39 40 41 /** 42 * Windows rendition of the component. 43 * <p> 44 * <strong>Warning:</strong> 45 * Serialized objects of this class will not be compatible with 46 * future Swing releases. The current serialization support is appropriate 47 * for short term storage or RMI between applications running the same 48 * version of Swing. A future release of Swing will provide support for 49 * long term persistence. 50 */ 51 public class WindowsTabbedPaneUI extends BasicTabbedPaneUI { 52 /** 53 * Keys to use for forward focus traversal when the JComponent is 54 * managing focus. 55 */ 56 private static Set managingFocusForwardTraversalKeys; 57 58 /** 59 * Keys to use for backward focus traversal when the JComponent is 60 * managing focus. 61 */ 62 private static Set managingFocusBackwardTraversalKeys; 63 64 private boolean contentOpaque = true; 65 66 protected void installDefaults() { 67 super.installDefaults(); 68 contentOpaque = UIManager.getBoolean("TabbedPane.contentOpaque"); 69 70 // focus forward traversal key 71 if (managingFocusForwardTraversalKeys==null) { 72 managingFocusForwardTraversalKeys = new HashSet(); 73 managingFocusForwardTraversalKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)); 74 } 75 tabPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, managingFocusForwardTraversalKeys); 76 // focus backward traversal key 77 if (managingFocusBackwardTraversalKeys==null) { 78 managingFocusBackwardTraversalKeys = new HashSet(); 79 managingFocusBackwardTraversalKeys.add( KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK)); 80 } 81 tabPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, managingFocusBackwardTraversalKeys); 82 } 83 84 protected void uninstallDefaults() { 85 // sets the focus forward and backward traversal keys to null 86 // to restore the defaults 87 tabPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null); 88 tabPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null); 89 super.uninstallDefaults(); 90 } 91 92 public static ComponentUI createUI(JComponent c) { 93 return new WindowsTabbedPaneUI(); 94 } 95 96 protected void setRolloverTab(int index) { 97 // Rollover is only supported on XP 98 if (XPStyle.getXP() != null) { 99 int oldRolloverTab = getRolloverTab(); 100 super.setRolloverTab(index); 101 Rectangle r1 = null; 102 Rectangle r2 = null; 103 if ( (oldRolloverTab >= 0) && (oldRolloverTab < tabPane.getTabCount()) ) { 104 r1 = getTabBounds(tabPane, oldRolloverTab); 105 } 106 if (index >= 0) { 107 r2 = getTabBounds(tabPane, index); 108 } 109 if (r1 != null) { 110 if (r2 != null) { 111 tabPane.repaint(r1.union(r2)); 112 } else { 113 tabPane.repaint(r1); 114 } 115 } else if (r2 != null) { 116 tabPane.repaint(r2); 117 } 118 } 119 } 120 121 protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { 122 XPStyle xp = XPStyle.getXP(); 123 if (xp != null && (contentOpaque || tabPane.isOpaque())) { 124 Skin skin = xp.getSkin(tabPane, Part.TABP_PANE); 125 if (skin != null) { 126 Insets insets = tabPane.getInsets(); 127 // Note: don't call getTabAreaInsets(), because it causes rotation. 128 // Make sure "TabbedPane.tabsOverlapBorder" is set to true in WindowsLookAndFeel 129 Insets tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets"); 130 int x = insets.left; 131 int y = insets.top; 132 int w = tabPane.getWidth() - insets.right - insets.left; 133 int h = tabPane.getHeight() - insets.top - insets.bottom; 134 135 // Expand area by tabAreaInsets.bottom to allow tabs to overlap onto the border. 136 if (tabPlacement == LEFT || tabPlacement == RIGHT) { 137 int tabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); 138 if (tabPlacement == LEFT) { 139 x += (tabWidth - tabAreaInsets.bottom); 140 } 141 w -= (tabWidth - tabAreaInsets.bottom); 142 } else { 143 int tabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); 144 if (tabPlacement == TOP) { 145 y += (tabHeight - tabAreaInsets.bottom); 146 } 147 h -= (tabHeight - tabAreaInsets.bottom); 148 } 149 150 paintRotatedSkin(g, skin, tabPlacement, x, y, w, h, null); 151 return; 152 } 153 } 154 super.paintContentBorder(g, tabPlacement, selectedIndex); 155 } 156 157 protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, 158 int x, int y, int w, int h, boolean isSelected ) { 159 if (XPStyle.getXP() == null) { 160 super.paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected); 161 } 162 } 163 164 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, 165 int x, int y, int w, int h, boolean isSelected ) { 166 XPStyle xp = XPStyle.getXP(); 167 if (xp != null) { 168 Part part; 169 170 int tabCount = tabPane.getTabCount(); 171 int tabRun = getRunForTab(tabCount, tabIndex); 172 if (tabRuns[tabRun] == tabIndex) { 173 part = Part.TABP_TABITEMLEFTEDGE; 174 } else if (tabCount > 1 && lastTabInRun(tabCount, tabRun) == tabIndex) { 175 part = Part.TABP_TABITEMRIGHTEDGE; 176 if (isSelected) { 177 // Align with right edge 178 if (tabPlacement == TOP || tabPlacement == BOTTOM) { 179 w++; 180 } else { 181 h++; 182 } 183 } 184 } else { 185 part = Part.TABP_TABITEM; 186 } 187 188 State state = State.NORMAL; 189 if (isSelected) { 190 state = State.SELECTED; 191 } else if (tabIndex == getRolloverTab()) { 192 state = State.HOT; 193 } 194 195 paintRotatedSkin(g, xp.getSkin(tabPane, part), tabPlacement, x, y, w, h, state); 196 } else { 197 super.paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected); 198 } 199 } 200 201 private void paintRotatedSkin(Graphics g, Skin skin, int tabPlacement, 202 int x, int y, int w, int h, State state) { 203 Graphics2D g2d = (Graphics2D)g.create(); 204 g2d.translate(x, y); 205 switch (tabPlacement) { 206 case RIGHT: g2d.translate(w, 0); 207 g2d.rotate(Math.toRadians(90.0)); 208 skin.paintSkin(g2d, 0, 0, h, w, state); 209 break; 210 211 case LEFT: g2d.scale(-1.0, 1.0); 212 g2d.rotate(Math.toRadians(90.0)); 213 skin.paintSkin(g2d, 0, 0, h, w, state); 214 break; 215 216 case BOTTOM: g2d.translate(0, h); 217 g2d.scale(-1.0, 1.0); 218 g2d.rotate(Math.toRadians(180.0)); 219 skin.paintSkin(g2d, 0, 0, w, h, state); 220 break; 221 222 case TOP: 223 default: skin.paintSkin(g2d, 0, 0, w, h, state); 224 } 225 g2d.dispose(); 226 } 227 }