1 /* 2 * Copyright (c) 1998, 2014, 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 javax.swing.plaf.metal; 27 28 import java.awt.*; 29 import javax.swing.*; 30 import javax.swing.border.*; 31 import javax.swing.plaf.basic.*; 32 33 34 /** 35 * Metal's split pane divider 36 * <p> 37 * <strong>Warning:</strong> 38 * Serialized objects of this class will not be compatible with 39 * future Swing releases. The current serialization support is 40 * appropriate for short term storage or RMI between applications running 41 * the same version of Swing. As of 1.4, support for long term storage 42 * of all JavaBeans™ 43 * has been added to the <code>java.beans</code> package. 44 * Please see {@link java.beans.XMLEncoder}. 45 * 46 * @author Steve Wilson 47 * @author Ralph kar 48 */ 49 @SuppressWarnings("serial") // Same-version serialization only 50 class MetalSplitPaneDivider extends BasicSplitPaneDivider 51 { 52 private MetalBumps bumps = new MetalBumps(10, 10, 53 MetalLookAndFeel.getControlHighlight(), 54 MetalLookAndFeel.getControlDarkShadow(), 55 MetalLookAndFeel.getControl() ); 56 57 private MetalBumps focusBumps = new MetalBumps(10, 10, 58 MetalLookAndFeel.getPrimaryControlHighlight(), 59 MetalLookAndFeel.getPrimaryControlDarkShadow(), 60 UIManager.getColor("SplitPane.dividerFocusColor")); 61 62 private int inset = 2; 63 64 private Color controlColor = MetalLookAndFeel.getControl(); 65 private Color primaryControlColor = UIManager.getColor( 66 "SplitPane.dividerFocusColor"); 67 68 public MetalSplitPaneDivider(BasicSplitPaneUI ui) { 69 super(ui); 70 } 71 72 public void paint(Graphics g) { 73 MetalBumps usedBumps; 74 if (splitPane.hasFocus()) { 75 usedBumps = focusBumps; 76 g.setColor(primaryControlColor); 77 } 78 else { 79 usedBumps = bumps; 80 g.setColor(controlColor); 81 } 82 Rectangle clip = g.getClipBounds(); 83 Insets insets = getInsets(); 84 g.fillRect(clip.x, clip.y, clip.width, clip.height); 85 Dimension size = getSize(); 86 size.width -= inset * 2; 87 size.height -= inset * 2; 88 int drawX = inset; 89 int drawY = inset; 90 if (insets != null) { 91 size.width -= (insets.left + insets.right); 92 size.height -= (insets.top + insets.bottom); 93 drawX += insets.left; 94 drawY += insets.top; 95 } 96 usedBumps.setBumpArea(size); 97 usedBumps.paintIcon(this, g, drawX, drawY); 98 super.paint(g); 99 } 100 101 /** 102 * Creates and return an instance of JButton that can be used to 103 * collapse the left component in the metal split pane. 104 */ 105 protected JButton createLeftOneTouchButton() { 106 JButton b = new JButton() { 107 // Sprite buffer for the arrow image of the left button 108 int[][] buffer = {{0, 0, 0, 2, 2, 0, 0, 0, 0}, 109 {0, 0, 2, 1, 1, 1, 0, 0, 0}, 110 {0, 2, 1, 1, 1, 1, 1, 0, 0}, 111 {2, 1, 1, 1, 1, 1, 1, 1, 0}, 112 {0, 3, 3, 3, 3, 3, 3, 3, 3}}; 113 114 public void setBorder(Border b) { 115 } 116 117 public void paint(Graphics g) { 118 JSplitPane splitPane = getSplitPaneFromSuper(); 119 if(splitPane != null) { 120 int oneTouchSize = getOneTouchSizeFromSuper(); 121 int orientation = getOrientationFromSuper(); 122 int blockSize = Math.min(getDividerSize(), 123 oneTouchSize); 124 125 // Initialize the color array 126 Color[] colors = { 127 this.getBackground(), 128 MetalLookAndFeel.getPrimaryControlDarkShadow(), 129 MetalLookAndFeel.getPrimaryControlInfo(), 130 MetalLookAndFeel.getPrimaryControlHighlight()}; 131 132 // Fill the background first ... 133 g.setColor(this.getBackground()); 134 if (isOpaque()) { 135 g.fillRect(0, 0, this.getWidth(), 136 this.getHeight()); 137 } 138 139 // ... then draw the arrow. 140 if (getModel().isPressed()) { 141 // Adjust color mapping for pressed button state 142 colors[1] = colors[2]; 143 } 144 if(orientation == JSplitPane.VERTICAL_SPLIT) { 145 // Draw the image for a vertical split 146 for (int i=1; i<=buffer[0].length; i++) { 147 for (int j=1; j<blockSize; j++) { 148 if (buffer[j-1][i-1] == 0) { 149 continue; 150 } 151 else { 152 g.setColor( 153 colors[buffer[j-1][i-1]]); 154 } 155 g.drawLine(i, j, i, j); 156 } 157 } 158 } 159 else { 160 // Draw the image for a horizontal split 161 // by simply swaping the i and j axis. 162 // Except the drawLine() call this code is 163 // identical to the code block above. This was done 164 // in order to remove the additional orientation 165 // check for each pixel. 166 for (int i=1; i<=buffer[0].length; i++) { 167 for (int j=1; j<blockSize; j++) { 168 if (buffer[j-1][i-1] == 0) { 169 // Nothing needs 170 // to be drawn 171 continue; 172 } 173 else { 174 // Set the color from the 175 // color map 176 g.setColor( 177 colors[buffer[j-1][i-1]]); 178 } 179 // Draw a pixel 180 g.drawLine(j, i, j, i); 181 } 182 } 183 } 184 } 185 } 186 187 // Don't want the button to participate in focus traversable. 188 public boolean isFocusTraversable() { 189 return false; 190 } 191 }; 192 b.setRequestFocusEnabled(false); 193 b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 194 b.setFocusPainted(false); 195 b.setBorderPainted(false); 196 maybeMakeButtonOpaque(b); 197 return b; 198 } 199 200 /** 201 * If necessary <code>c</code> is made opaque. 202 */ 203 private void maybeMakeButtonOpaque(JComponent c) { 204 Object opaque = UIManager.get("SplitPane.oneTouchButtonsOpaque"); 205 if (opaque != null) { 206 c.setOpaque(((Boolean)opaque).booleanValue()); 207 } 208 } 209 210 /** 211 * Creates and return an instance of JButton that can be used to 212 * collapse the right component in the metal split pane. 213 */ 214 protected JButton createRightOneTouchButton() { 215 JButton b = new JButton() { 216 // Sprite buffer for the arrow image of the right button 217 int[][] buffer = {{2, 2, 2, 2, 2, 2, 2, 2}, 218 {0, 1, 1, 1, 1, 1, 1, 3}, 219 {0, 0, 1, 1, 1, 1, 3, 0}, 220 {0, 0, 0, 1, 1, 3, 0, 0}, 221 {0, 0, 0, 0, 3, 0, 0, 0}}; 222 223 public void setBorder(Border border) { 224 } 225 226 public void paint(Graphics g) { 227 JSplitPane splitPane = getSplitPaneFromSuper(); 228 if(splitPane != null) { 229 int oneTouchSize = getOneTouchSizeFromSuper(); 230 int orientation = getOrientationFromSuper(); 231 int blockSize = Math.min(getDividerSize(), 232 oneTouchSize); 233 234 // Initialize the color array 235 Color[] colors = { 236 this.getBackground(), 237 MetalLookAndFeel.getPrimaryControlDarkShadow(), 238 MetalLookAndFeel.getPrimaryControlInfo(), 239 MetalLookAndFeel.getPrimaryControlHighlight()}; 240 241 // Fill the background first ... 242 g.setColor(this.getBackground()); 243 if (isOpaque()) { 244 g.fillRect(0, 0, this.getWidth(), 245 this.getHeight()); 246 } 247 248 // ... then draw the arrow. 249 if (getModel().isPressed()) { 250 // Adjust color mapping for pressed button state 251 colors[1] = colors[2]; 252 } 253 if(orientation == JSplitPane.VERTICAL_SPLIT) { 254 // Draw the image for a vertical split 255 for (int i=1; i<=buffer[0].length; i++) { 256 for (int j=1; j<blockSize; j++) { 257 if (buffer[j-1][i-1] == 0) { 258 continue; 259 } 260 else { 261 g.setColor( 262 colors[buffer[j-1][i-1]]); 263 } 264 g.drawLine(i, j, i, j); 265 } 266 } 267 } 268 else { 269 // Draw the image for a horizontal split 270 // by simply swaping the i and j axis. 271 // Except the drawLine() call this code is 272 // identical to the code block above. This was done 273 // in order to remove the additional orientation 274 // check for each pixel. 275 for (int i=1; i<=buffer[0].length; i++) { 276 for (int j=1; j<blockSize; j++) { 277 if (buffer[j-1][i-1] == 0) { 278 // Nothing needs 279 // to be drawn 280 continue; 281 } 282 else { 283 // Set the color from the 284 // color map 285 g.setColor( 286 colors[buffer[j-1][i-1]]); 287 } 288 // Draw a pixel 289 g.drawLine(j, i, j, i); 290 } 291 } 292 } 293 } 294 } 295 296 // Don't want the button to participate in focus traversable. 297 public boolean isFocusTraversable() { 298 return false; 299 } 300 }; 301 b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 302 b.setFocusPainted(false); 303 b.setBorderPainted(false); 304 b.setRequestFocusEnabled(false); 305 maybeMakeButtonOpaque(b); 306 return b; 307 } 308 309 /** 310 * Used to layout a MetalSplitPaneDivider. Layout for the divider 311 * involves appropriately moving the left/right buttons around. 312 * <p> 313 * This class should be treated as a "protected" inner class. 314 * Instantiate it only within subclasses of MetalSplitPaneDivider. 315 */ 316 public class MetalDividerLayout implements LayoutManager { 317 318 // NOTE NOTE NOTE NOTE NOTE 319 // This class is no longer used, the functionality has 320 // been rolled into BasicSplitPaneDivider.DividerLayout as a 321 // defaults property 322 323 public void layoutContainer(Container c) { 324 JButton leftButton = getLeftButtonFromSuper(); 325 JButton rightButton = getRightButtonFromSuper(); 326 JSplitPane splitPane = getSplitPaneFromSuper(); 327 int orientation = getOrientationFromSuper(); 328 int oneTouchSize = getOneTouchSizeFromSuper(); 329 int oneTouchOffset = getOneTouchOffsetFromSuper(); 330 Insets insets = getInsets(); 331 332 // This layout differs from the one used in BasicSplitPaneDivider. 333 // It does not center justify the oneTouchExpadable buttons. 334 // This was necessary in order to meet the spec of the Metal 335 // splitpane divider. 336 if (leftButton != null && rightButton != null && 337 c == MetalSplitPaneDivider.this) { 338 if (splitPane.isOneTouchExpandable()) { 339 if (orientation == JSplitPane.VERTICAL_SPLIT) { 340 int extraY = (insets != null) ? insets.top : 0; 341 int blockSize = getDividerSize(); 342 343 if (insets != null) { 344 blockSize -= (insets.top + insets.bottom); 345 } 346 blockSize = Math.min(blockSize, oneTouchSize); 347 leftButton.setBounds(oneTouchOffset, extraY, 348 blockSize * 2, blockSize); 349 rightButton.setBounds(oneTouchOffset + 350 oneTouchSize * 2, extraY, 351 blockSize * 2, blockSize); 352 } 353 else { 354 int blockSize = getDividerSize(); 355 int extraX = (insets != null) ? insets.left : 0; 356 357 if (insets != null) { 358 blockSize -= (insets.left + insets.right); 359 } 360 blockSize = Math.min(blockSize, oneTouchSize); 361 leftButton.setBounds(extraX, oneTouchOffset, 362 blockSize, blockSize * 2); 363 rightButton.setBounds(extraX, oneTouchOffset + 364 oneTouchSize * 2, blockSize, 365 blockSize * 2); 366 } 367 } 368 else { 369 leftButton.setBounds(-5, -5, 1, 1); 370 rightButton.setBounds(-5, -5, 1, 1); 371 } 372 } 373 } 374 375 public Dimension minimumLayoutSize(Container c) { 376 return new Dimension(0,0); 377 } 378 379 public Dimension preferredLayoutSize(Container c) { 380 return new Dimension(0, 0); 381 } 382 383 public void removeLayoutComponent(Component c) {} 384 385 public void addLayoutComponent(String string, Component c) {} 386 } 387 388 /* 389 * The following methods only exist in order to be able to access protected 390 * members in the superclass, because these are otherwise not available 391 * in any inner class. 392 */ 393 394 int getOneTouchSizeFromSuper() { 395 return BasicSplitPaneDivider.ONE_TOUCH_SIZE; 396 } 397 398 int getOneTouchOffsetFromSuper() { 399 return BasicSplitPaneDivider.ONE_TOUCH_OFFSET; 400 } 401 402 int getOrientationFromSuper() { 403 return super.orientation; 404 } 405 406 JSplitPane getSplitPaneFromSuper() { 407 return super.splitPane; 408 } 409 410 JButton getLeftButtonFromSuper() { 411 return super.leftButton; 412 } 413 414 JButton getRightButtonFromSuper() { 415 return super.rightButton; 416 } 417 }