1 /* 2 * Copyright (c) 2011, 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 com.apple.laf; 27 28 import java.awt.*; 29 import java.beans.PropertyChangeEvent; 30 31 import javax.swing.*; 32 import javax.swing.border.Border; 33 import javax.swing.plaf.basic.BasicSplitPaneDivider; 34 35 import apple.laf.*; 36 import apple.laf.JRSUIConstants.State; 37 38 import com.apple.laf.AquaUtils.LazyKeyedSingleton; 39 import com.apple.laf.AquaUtils.RecyclableSingleton; 40 import com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor; 41 42 @SuppressWarnings("serial") // Superclass is not serializable across versions 43 public class AquaSplitPaneDividerUI extends BasicSplitPaneDivider { 44 final AquaPainter<JRSUIState> painter = AquaPainter.create(JRSUIStateFactory.getSplitPaneDivider()); 45 AquaSplitPaneDividerUI(final AquaSplitPaneUI ui)46 public AquaSplitPaneDividerUI(final AquaSplitPaneUI ui) { 47 super(ui); 48 setLayout(new AquaSplitPaneDividerUI.DividerLayout()); 49 } 50 51 /** 52 * Property change event, presumably from the JSplitPane, will message 53 * updateOrientation if necessary. 54 */ propertyChange(final PropertyChangeEvent e)55 public void propertyChange(final PropertyChangeEvent e) { 56 if (e.getSource() == splitPane) { 57 final String propName = e.getPropertyName(); 58 if ("enabled".equals(propName)) { 59 final boolean enabled = splitPane.isEnabled(); 60 if (leftButton != null) leftButton.setEnabled(enabled); 61 if (rightButton != null) rightButton.setEnabled(enabled); 62 } else if (JSplitPane.ORIENTATION_PROPERTY.equals(propName)) { 63 // need to regenerate the buttons, since we bake the orientation into them 64 if (rightButton != null) { 65 remove(rightButton); rightButton = null; 66 } 67 if (leftButton != null) { 68 remove(leftButton); leftButton = null; 69 } 70 oneTouchExpandableChanged(); 71 } 72 } 73 super.propertyChange(e); 74 } 75 getMaxDividerSize()76 public int getMaxDividerSize() { 77 return 10; 78 } 79 80 /** 81 * Paints the divider. 82 */ paint(final Graphics g)83 public void paint(final Graphics g) { 84 final Dimension size = getSize(); 85 int x = 0; 86 int y = 0; 87 88 final boolean horizontal = splitPane.getOrientation() == SwingConstants.HORIZONTAL; 89 //System.err.println("Size = " + size + " orientation horiz = " + horizontal); 90 // size determines orientation 91 final int maxSize = getMaxDividerSize(); 92 boolean doPaint = true; 93 if (horizontal) { 94 if (size.height > maxSize) { 95 final int diff = size.height - maxSize; 96 y = diff / 2; 97 size.height = maxSize; 98 } 99 if (size.height < 4) doPaint = false; 100 } else { 101 if (size.width > maxSize) { 102 final int diff = size.width - maxSize; 103 x = diff / 2; 104 size.width = maxSize; 105 } 106 if (size.width < 4) doPaint = false; 107 } 108 109 if (doPaint) { 110 painter.state.set(getState()); 111 painter.paint(g, splitPane, x, y, size.width, size.height); 112 } 113 114 super.paint(g); // Ends up at Container.paint, which paints our JButton children 115 } 116 getState()117 protected State getState() { 118 return splitPane.isEnabled() ? State.ACTIVE : State.DISABLED; 119 } 120 createLeftOneTouchButton()121 protected JButton createLeftOneTouchButton() { 122 return createButtonForDirection(getDirection(true)); 123 } 124 createRightOneTouchButton()125 protected JButton createRightOneTouchButton() { 126 return createButtonForDirection(getDirection(false)); 127 } 128 129 static final LazyKeyedSingleton<Integer, Image> directionArrows = new LazyKeyedSingleton<Integer, Image>() { 130 protected Image getInstance(final Integer direction) { 131 final Image arrowImage = AquaImageFactory.getArrowImageForDirection(direction); 132 final int h = (arrowImage.getHeight(null) * 5) / 7; 133 final int w = (arrowImage.getWidth(null) * 5) / 7; 134 return AquaUtils.generateLightenedImage(arrowImage.getScaledInstance(w, h, Image.SCALE_SMOOTH), 50); 135 } 136 }; 137 138 // separate static, because the divider needs to be serializable 139 // see <rdar://problem/7590946> JSplitPane is not serializable when using Aqua look and feel createButtonForDirection(final int direction)140 static JButton createButtonForDirection(final int direction) { 141 final JButton button = new JButton(new ImageIcon(directionArrows.get(Integer.valueOf(direction)))); 142 button.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 143 button.setFocusPainted(false); 144 button.setRequestFocusEnabled(false); 145 button.setFocusable(false); 146 button.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 147 return button; 148 } 149 getDirection(final boolean isLeft)150 int getDirection(final boolean isLeft) { 151 if (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { 152 return isLeft ? SwingConstants.WEST : SwingConstants.EAST; 153 } 154 155 return isLeft ? SwingConstants.NORTH : SwingConstants.SOUTH; 156 } 157 158 static final int kMaxPopupArrowSize = 9; 159 protected class DividerLayout extends BasicSplitPaneDivider.DividerLayout { layoutContainer(final Container c)160 public void layoutContainer(final Container c) { 161 final int maxSize = getMaxDividerSize(); 162 final Dimension size = getSize(); 163 164 if (leftButton == null || rightButton == null || c != AquaSplitPaneDividerUI.this) return; 165 166 if (!splitPane.isOneTouchExpandable()) { 167 leftButton.setBounds(-5, -5, 1, 1); 168 rightButton.setBounds(-5, -5, 1, 1); 169 return; 170 } 171 172 final int blockSize = Math.min(getDividerSize(), kMaxPopupArrowSize); // make it 1 less than divider, or kMaxPopupArrowSize 173 174 // put them at the right or the bottom 175 if (orientation == JSplitPane.VERTICAL_SPLIT) { 176 int yPosition = 0; 177 if (size.height > maxSize) { 178 final int diff = size.height - maxSize; 179 yPosition = diff / 2; 180 } 181 int xPosition = kMaxPopupArrowSize + ONE_TOUCH_OFFSET; 182 183 rightButton.setBounds(xPosition, yPosition, kMaxPopupArrowSize, blockSize); 184 185 xPosition -= (kMaxPopupArrowSize + ONE_TOUCH_OFFSET); 186 leftButton.setBounds(xPosition, yPosition, kMaxPopupArrowSize, blockSize); 187 } else { 188 int xPosition = 0; 189 if (size.width > maxSize) { 190 final int diff = size.width - maxSize; 191 xPosition = diff / 2; 192 } 193 int yPosition = kMaxPopupArrowSize + ONE_TOUCH_OFFSET; 194 195 rightButton.setBounds(xPosition, yPosition, blockSize, kMaxPopupArrowSize); 196 197 yPosition -= (kMaxPopupArrowSize + ONE_TOUCH_OFFSET); 198 leftButton.setBounds(xPosition, yPosition, blockSize, kMaxPopupArrowSize); 199 } 200 } 201 } 202 getHorizontalSplitDividerGradientVariant()203 public static Border getHorizontalSplitDividerGradientVariant() { 204 return HorizontalSplitDividerGradientPainter.instance(); 205 } 206 207 static class HorizontalSplitDividerGradientPainter implements Border { 208 private static final RecyclableSingleton<HorizontalSplitDividerGradientPainter> instance = new RecyclableSingletonFromDefaultConstructor<HorizontalSplitDividerGradientPainter>(HorizontalSplitDividerGradientPainter.class); instance()209 static HorizontalSplitDividerGradientPainter instance() { 210 return instance.get(); 211 } 212 213 final Color startColor = Color.white; 214 final Color endColor = new Color(217, 217, 217); 215 final Color borderLines = Color.lightGray; 216 getBorderInsets(final Component c)217 public Insets getBorderInsets(final Component c) { 218 return new Insets(0, 0, 0, 0); 219 } 220 isBorderOpaque()221 public boolean isBorderOpaque() { 222 return true; 223 } 224 paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height)225 public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) { 226 if (!(g instanceof Graphics2D)) return; 227 228 final Graphics2D g2d = (Graphics2D)g; 229 final Color oldColor = g2d.getColor(); 230 231 g2d.setPaint(new GradientPaint(0, 0, startColor, 0, height, endColor)); 232 g2d.fillRect(x, y, width, height); 233 g2d.setColor(borderLines); 234 g2d.drawLine(x, y, x + width, y); 235 g2d.drawLine(x, y + height - 1, x + width, y + height - 1); 236 237 g2d.setColor(oldColor); 238 } 239 } 240 } 241