| /* | |
| * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package javax.swing.plaf.metal; | |
| import java.awt.*; | |
| import javax.swing.*; | |
| import javax.swing.border.*; | |
| import javax.swing.plaf.basic.*; | |
| /** | |
| * Metal's split pane divider | |
| * <p> | |
| * <strong>Warning:</strong> | |
| * Serialized objects of this class will not be compatible with | |
| * future Swing releases. The current serialization support is | |
| * appropriate for short term storage or RMI between applications running | |
| * the same version of Swing. As of 1.4, support for long term storage | |
| * of all JavaBeans | |
| * has been added to the <code>java.beans</code> package. | |
| * Please see {@link java.beans.XMLEncoder}. | |
| * | |
| * @author Steve Wilson | |
| * @author Ralph kar | |
| */ | |
| @SuppressWarnings("serial") // Same-version serialization only | |
| class MetalSplitPaneDivider extends BasicSplitPaneDivider | |
| { | |
| private MetalBumps bumps = new MetalBumps(10, 10, | |
| MetalLookAndFeel.getControlHighlight(), | |
| MetalLookAndFeel.getControlDarkShadow(), | |
| MetalLookAndFeel.getControl() ); | |
| private MetalBumps focusBumps = new MetalBumps(10, 10, | |
| MetalLookAndFeel.getPrimaryControlHighlight(), | |
| MetalLookAndFeel.getPrimaryControlDarkShadow(), | |
| UIManager.getColor("SplitPane.dividerFocusColor")); | |
| private int inset = 2; | |
| private Color controlColor = MetalLookAndFeel.getControl(); | |
| private Color primaryControlColor = UIManager.getColor( | |
| "SplitPane.dividerFocusColor"); | |
| public MetalSplitPaneDivider(BasicSplitPaneUI ui) { | |
| super(ui); | |
| } | |
| public void paint(Graphics g) { | |
| MetalBumps usedBumps; | |
| if (splitPane.hasFocus()) { | |
| usedBumps = focusBumps; | |
| g.setColor(primaryControlColor); | |
| } | |
| else { | |
| usedBumps = bumps; | |
| g.setColor(controlColor); | |
| } | |
| Rectangle clip = g.getClipBounds(); | |
| Insets insets = getInsets(); | |
| g.fillRect(clip.x, clip.y, clip.width, clip.height); | |
| Dimension size = getSize(); | |
| size.width -= inset * 2; | |
| size.height -= inset * 2; | |
| int drawX = inset; | |
| int drawY = inset; | |
| if (insets != null) { | |
| size.width -= (insets.left + insets.right); | |
| size.height -= (insets.top + insets.bottom); | |
| drawX += insets.left; | |
| drawY += insets.top; | |
| } | |
| usedBumps.setBumpArea(size); | |
| usedBumps.paintIcon(this, g, drawX, drawY); | |
| super.paint(g); | |
| } | |
| /** | |
| * Creates and return an instance of JButton that can be used to | |
| * collapse the left component in the metal split pane. | |
| */ | |
| protected JButton createLeftOneTouchButton() { | |
| JButton b = new JButton() { | |
| // Sprite buffer for the arrow image of the left button | |
| int[][] buffer = {{0, 0, 0, 2, 2, 0, 0, 0, 0}, | |
| {0, 0, 2, 1, 1, 1, 0, 0, 0}, | |
| {0, 2, 1, 1, 1, 1, 1, 0, 0}, | |
| {2, 1, 1, 1, 1, 1, 1, 1, 0}, | |
| {0, 3, 3, 3, 3, 3, 3, 3, 3}}; | |
| public void setBorder(Border b) { | |
| } | |
| public void paint(Graphics g) { | |
| JSplitPane splitPane = getSplitPaneFromSuper(); | |
| if(splitPane != null) { | |
| int oneTouchSize = getOneTouchSizeFromSuper(); | |
| int orientation = getOrientationFromSuper(); | |
| int blockSize = Math.min(getDividerSize(), | |
| oneTouchSize); | |
| // Initialize the color array | |
| Color[] colors = { | |
| this.getBackground(), | |
| MetalLookAndFeel.getPrimaryControlDarkShadow(), | |
| MetalLookAndFeel.getPrimaryControlInfo(), | |
| MetalLookAndFeel.getPrimaryControlHighlight()}; | |
| // Fill the background first ... | |
| g.setColor(this.getBackground()); | |
| if (isOpaque()) { | |
| g.fillRect(0, 0, this.getWidth(), | |
| this.getHeight()); | |
| } | |
| // ... then draw the arrow. | |
| if (getModel().isPressed()) { | |
| // Adjust color mapping for pressed button state | |
| colors[1] = colors[2]; | |
| } | |
| if(orientation == JSplitPane.VERTICAL_SPLIT) { | |
| // Draw the image for a vertical split | |
| for (int i=1; i<=buffer[0].length; i++) { | |
| for (int j=1; j<blockSize; j++) { | |
| if (buffer[j-1][i-1] == 0) { | |
| continue; | |
| } | |
| else { | |
| g.setColor( | |
| colors[buffer[j-1][i-1]]); | |
| } | |
| g.drawLine(i, j, i, j); | |
| } | |
| } | |
| } | |
| else { | |
| // Draw the image for a horizontal split | |
| // by simply swapping the i and j axis. | |
| // Except the drawLine() call this code is | |
| // identical to the code block above. This was done | |
| // in order to remove the additional orientation | |
| // check for each pixel. | |
| for (int i=1; i<=buffer[0].length; i++) { | |
| for (int j=1; j<blockSize; j++) { | |
| if (buffer[j-1][i-1] == 0) { | |
| // Nothing needs | |
| // to be drawn | |
| continue; | |
| } | |
| else { | |
| // Set the color from the | |
| // color map | |
| g.setColor( | |
| colors[buffer[j-1][i-1]]); | |
| } | |
| // Draw a pixel | |
| g.drawLine(j, i, j, i); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Don't want the button to participate in focus traversable. | |
| @SuppressWarnings("deprecation") | |
| public boolean isFocusTraversable() { | |
| return false; | |
| } | |
| }; | |
| b.setRequestFocusEnabled(false); | |
| b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); | |
| b.setFocusPainted(false); | |
| b.setBorderPainted(false); | |
| maybeMakeButtonOpaque(b); | |
| return b; | |
| } | |
| /** | |
| * If necessary <code>c</code> is made opaque. | |
| */ | |
| private void maybeMakeButtonOpaque(JComponent c) { | |
| Object opaque = UIManager.get("SplitPane.oneTouchButtonsOpaque"); | |
| if (opaque != null) { | |
| c.setOpaque(((Boolean)opaque).booleanValue()); | |
| } | |
| } | |
| /** | |
| * Creates and return an instance of JButton that can be used to | |
| * collapse the right component in the metal split pane. | |
| */ | |
| protected JButton createRightOneTouchButton() { | |
| JButton b = new JButton() { | |
| // Sprite buffer for the arrow image of the right button | |
| int[][] buffer = {{2, 2, 2, 2, 2, 2, 2, 2}, | |
| {0, 1, 1, 1, 1, 1, 1, 3}, | |
| {0, 0, 1, 1, 1, 1, 3, 0}, | |
| {0, 0, 0, 1, 1, 3, 0, 0}, | |
| {0, 0, 0, 0, 3, 0, 0, 0}}; | |
| public void setBorder(Border border) { | |
| } | |
| public void paint(Graphics g) { | |
| JSplitPane splitPane = getSplitPaneFromSuper(); | |
| if(splitPane != null) { | |
| int oneTouchSize = getOneTouchSizeFromSuper(); | |
| int orientation = getOrientationFromSuper(); | |
| int blockSize = Math.min(getDividerSize(), | |
| oneTouchSize); | |
| // Initialize the color array | |
| Color[] colors = { | |
| this.getBackground(), | |
| MetalLookAndFeel.getPrimaryControlDarkShadow(), | |
| MetalLookAndFeel.getPrimaryControlInfo(), | |
| MetalLookAndFeel.getPrimaryControlHighlight()}; | |
| // Fill the background first ... | |
| g.setColor(this.getBackground()); | |
| if (isOpaque()) { | |
| g.fillRect(0, 0, this.getWidth(), | |
| this.getHeight()); | |
| } | |
| // ... then draw the arrow. | |
| if (getModel().isPressed()) { | |
| // Adjust color mapping for pressed button state | |
| colors[1] = colors[2]; | |
| } | |
| if(orientation == JSplitPane.VERTICAL_SPLIT) { | |
| // Draw the image for a vertical split | |
| for (int i=1; i<=buffer[0].length; i++) { | |
| for (int j=1; j<blockSize; j++) { | |
| if (buffer[j-1][i-1] == 0) { | |
| continue; | |
| } | |
| else { | |
| g.setColor( | |
| colors[buffer[j-1][i-1]]); | |
| } | |
| g.drawLine(i, j, i, j); | |
| } | |
| } | |
| } | |
| else { | |
| // Draw the image for a horizontal split | |
| // by simply swapping the i and j axis. | |
| // Except the drawLine() call this code is | |
| // identical to the code block above. This was done | |
| // in order to remove the additional orientation | |
| // check for each pixel. | |
| for (int i=1; i<=buffer[0].length; i++) { | |
| for (int j=1; j<blockSize; j++) { | |
| if (buffer[j-1][i-1] == 0) { | |
| // Nothing needs | |
| // to be drawn | |
| continue; | |
| } | |
| else { | |
| // Set the color from the | |
| // color map | |
| g.setColor( | |
| colors[buffer[j-1][i-1]]); | |
| } | |
| // Draw a pixel | |
| g.drawLine(j, i, j, i); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Don't want the button to participate in focus traversable. | |
| @SuppressWarnings("deprecation") | |
| public boolean isFocusTraversable() { | |
| return false; | |
| } | |
| }; | |
| b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); | |
| b.setFocusPainted(false); | |
| b.setBorderPainted(false); | |
| b.setRequestFocusEnabled(false); | |
| maybeMakeButtonOpaque(b); | |
| return b; | |
| } | |
| /** | |
| * Used to layout a MetalSplitPaneDivider. Layout for the divider | |
| * involves appropriately moving the left/right buttons around. | |
| * <p> | |
| * This class should be treated as a "protected" inner class. | |
| * Instantiate it only within subclasses of MetalSplitPaneDivider. | |
| */ | |
| public class MetalDividerLayout implements LayoutManager { | |
| // NOTE NOTE NOTE NOTE NOTE | |
| // This class is no longer used, the functionality has | |
| // been rolled into BasicSplitPaneDivider.DividerLayout as a | |
| // defaults property | |
| public void layoutContainer(Container c) { | |
| JButton leftButton = getLeftButtonFromSuper(); | |
| JButton rightButton = getRightButtonFromSuper(); | |
| JSplitPane splitPane = getSplitPaneFromSuper(); | |
| int orientation = getOrientationFromSuper(); | |
| int oneTouchSize = getOneTouchSizeFromSuper(); | |
| int oneTouchOffset = getOneTouchOffsetFromSuper(); | |
| Insets insets = getInsets(); | |
| // This layout differs from the one used in BasicSplitPaneDivider. | |
| // It does not center justify the oneTouchExpandable buttons. | |
| // This was necessary in order to meet the spec of the Metal | |
| // splitpane divider. | |
| if (leftButton != null && rightButton != null && | |
| c == MetalSplitPaneDivider.this) { | |
| if (splitPane.isOneTouchExpandable()) { | |
| if (orientation == JSplitPane.VERTICAL_SPLIT) { | |
| int extraY = (insets != null) ? insets.top : 0; | |
| int blockSize = getDividerSize(); | |
| if (insets != null) { | |
| blockSize -= (insets.top + insets.bottom); | |
| } | |
| blockSize = Math.min(blockSize, oneTouchSize); | |
| leftButton.setBounds(oneTouchOffset, extraY, | |
| blockSize * 2, blockSize); | |
| rightButton.setBounds(oneTouchOffset + | |
| oneTouchSize * 2, extraY, | |
| blockSize * 2, blockSize); | |
| } | |
| else { | |
| int blockSize = getDividerSize(); | |
| int extraX = (insets != null) ? insets.left : 0; | |
| if (insets != null) { | |
| blockSize -= (insets.left + insets.right); | |
| } | |
| blockSize = Math.min(blockSize, oneTouchSize); | |
| leftButton.setBounds(extraX, oneTouchOffset, | |
| blockSize, blockSize * 2); | |
| rightButton.setBounds(extraX, oneTouchOffset + | |
| oneTouchSize * 2, blockSize, | |
| blockSize * 2); | |
| } | |
| } | |
| else { | |
| leftButton.setBounds(-5, -5, 1, 1); | |
| rightButton.setBounds(-5, -5, 1, 1); | |
| } | |
| } | |
| } | |
| public Dimension minimumLayoutSize(Container c) { | |
| return new Dimension(0,0); | |
| } | |
| public Dimension preferredLayoutSize(Container c) { | |
| return new Dimension(0, 0); | |
| } | |
| public void removeLayoutComponent(Component c) {} | |
| public void addLayoutComponent(String string, Component c) {} | |
| } | |
| /* | |
| * The following methods only exist in order to be able to access protected | |
| * members in the superclass, because these are otherwise not available | |
| * in any inner class. | |
| */ | |
| int getOneTouchSizeFromSuper() { | |
| return BasicSplitPaneDivider.ONE_TOUCH_SIZE; | |
| } | |
| int getOneTouchOffsetFromSuper() { | |
| return BasicSplitPaneDivider.ONE_TOUCH_OFFSET; | |
| } | |
| int getOrientationFromSuper() { | |
| return super.orientation; | |
| } | |
| JSplitPane getSplitPaneFromSuper() { | |
| return super.splitPane; | |
| } | |
| JButton getLeftButtonFromSuper() { | |
| return super.leftButton; | |
| } | |
| JButton getRightButtonFromSuper() { | |
| return super.rightButton; | |
| } | |
| } |