1 /* 2 * Copyright (c) 1997, 2008, 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<KeyStroke> managingFocusForwardTraversalKeys; 57 58 /** 59 * Keys to use for backward focus traversal when the JComponent is 60 * managing focus. 61 */ 62 private static Set<KeyStroke> managingFocusBackwardTraversalKeys; 63 64 private boolean contentOpaque = true; 65 66 @SuppressWarnings("deprecation") 67 protected void installDefaults() { 68 super.installDefaults(); 69 contentOpaque = UIManager.getBoolean("TabbedPane.contentOpaque"); 70 71 // focus forward traversal key 72 if (managingFocusForwardTraversalKeys==null) { 73 managingFocusForwardTraversalKeys = new HashSet<KeyStroke>(); 74 managingFocusForwardTraversalKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)); 75 } 76 tabPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, managingFocusForwardTraversalKeys); 77 // focus backward traversal key 78 if (managingFocusBackwardTraversalKeys==null) { 79 managingFocusBackwardTraversalKeys = new HashSet<KeyStroke>(); 80 managingFocusBackwardTraversalKeys.add( KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK)); 81 } 82 tabPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, managingFocusBackwardTraversalKeys); 83 } 84 85 protected void uninstallDefaults() { 86 // sets the focus forward and backward traversal keys to null 87 // to restore the defaults 88 tabPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null); 89 tabPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null); 90 super.uninstallDefaults(); 91 } 92 93 public static ComponentUI createUI(JComponent c) { 94 return new WindowsTabbedPaneUI(); 95 } 96 97 protected void setRolloverTab(int index) { 98 // Rollover is only supported on XP 99 if (XPStyle.getXP() != null) { 100 int oldRolloverTab = getRolloverTab(); 101 super.setRolloverTab(index); 102 Rectangle r1 = null; 103 Rectangle r2 = null; 104 if ( (oldRolloverTab >= 0) && (oldRolloverTab < tabPane.getTabCount()) ) { 105 r1 = getTabBounds(tabPane, oldRolloverTab); 106 } 107 if (index >= 0) { 108 r2 = getTabBounds(tabPane, index); 109 } 110 if (r1 != null) { 111 if (r2 != null) { 112 tabPane.repaint(r1.union(r2)); 113 } else { 114 tabPane.repaint(r1); 115 } 116 } else if (r2 != null) { 117 tabPane.repaint(r2); 118 } 119 } 120 } 121 122 protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { 123 XPStyle xp = XPStyle.getXP(); 124 if (xp != null && (contentOpaque || tabPane.isOpaque())) { 125 Skin skin = xp.getSkin(tabPane, Part.TABP_PANE); 126 if (skin != null) { 127 Insets insets = tabPane.getInsets(); 128 // Note: don't call getTabAreaInsets(), because it causes rotation. 129 // Make sure "TabbedPane.tabsOverlapBorder" is set to true in WindowsLookAndFeel 130 Insets tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets"); 131 int x = insets.left; 132 int y = insets.top; 133 int w = tabPane.getWidth() - insets.right - insets.left; 134 int h = tabPane.getHeight() - insets.top - insets.bottom; 135 136 // Expand area by tabAreaInsets.bottom to allow tabs to overlap onto the border. 137 if (tabPlacement == LEFT || tabPlacement == RIGHT) { 138 int tabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); 139 if (tabPlacement == LEFT) { 140 x += (tabWidth - tabAreaInsets.bottom); 141 } 142 w -= (tabWidth - tabAreaInsets.bottom); 143 } else { 144 int tabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); 145 if (tabPlacement == TOP) { 146 y += (tabHeight - tabAreaInsets.bottom); 147 } 148 h -= (tabHeight - tabAreaInsets.bottom); 149 } 150 151 paintRotatedSkin(g, skin, tabPlacement, x, y, w, h, null); 152 return; 153 } 154 } 155 super.paintContentBorder(g, tabPlacement, selectedIndex); 156 } 157 158 protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, 159 int x, int y, int w, int h, boolean isSelected ) { 160 if (XPStyle.getXP() == null) { 161 super.paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected); 162 } 163 } 164 165 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, 166 int x, int y, int w, int h, boolean isSelected ) { 167 XPStyle xp = XPStyle.getXP(); 168 if (xp != null) { 169 Part part; 170 171 int tabCount = tabPane.getTabCount(); 172 int tabRun = getRunForTab(tabCount, tabIndex); 173 if (tabRuns[tabRun] == tabIndex) { 174 part = Part.TABP_TABITEMLEFTEDGE; 175 } else if (tabCount > 1 && lastTabInRun(tabCount, tabRun) == tabIndex) { 176 part = Part.TABP_TABITEMRIGHTEDGE; 177 if (isSelected) { 178 // Align with right edge 179 if (tabPlacement == TOP || tabPlacement == BOTTOM) { 180 w++; 181 } else { 182 h++; 183 } 184 } 185 } else { 186 part = Part.TABP_TABITEM; 187 } 188 189 State state = State.NORMAL; 190 if (isSelected) { 191 state = State.SELECTED; 192 } else if (tabIndex == getRolloverTab()) { 193 state = State.HOT; 194 } 195 196 paintRotatedSkin(g, xp.getSkin(tabPane, part), tabPlacement, x, y, w, h, state); 197 } else { 198 super.paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected); 199 } 200 } 201 202 private void paintRotatedSkin(Graphics g, Skin skin, int tabPlacement, 203 int x, int y, int w, int h, State state) { 204 Graphics2D g2d = (Graphics2D)g.create(); 205 g2d.translate(x, y); 206 switch (tabPlacement) { 207 case RIGHT: g2d.translate(w, 0); 208 g2d.rotate(Math.toRadians(90.0)); 209 skin.paintSkin(g2d, 0, 0, h, w, state); 210 break; 211 212 case LEFT: g2d.scale(-1.0, 1.0); 213 g2d.rotate(Math.toRadians(90.0)); 214 skin.paintSkin(g2d, 0, 0, h, w, state); 215 break; 216 217 case BOTTOM: g2d.translate(0, h); 218 g2d.scale(-1.0, 1.0); 219 g2d.rotate(Math.toRadians(180.0)); 220 skin.paintSkin(g2d, 0, 0, w, h, state); 221 break; 222 223 case TOP: 224 default: skin.paintSkin(g2d, 0, 0, w, h, state); 225 } 226 g2d.dispose(); 227 } 228 }