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&trade;
  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             @SuppressWarnings("deprecation")
 189             public boolean isFocusTraversable() {
 190                 return false;
 191             }
 192         };
 193         b.setRequestFocusEnabled(false);
 194         b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 195         b.setFocusPainted(false);
 196         b.setBorderPainted(false);
 197         maybeMakeButtonOpaque(b);
 198         return b;
 199     }
 200 
 201     /**
 202      * If necessary <code>c</code> is made opaque.
 203      */
 204     private void maybeMakeButtonOpaque(JComponent c) {
 205         Object opaque = UIManager.get("SplitPane.oneTouchButtonsOpaque");
 206         if (opaque != null) {
 207             c.setOpaque(((Boolean)opaque).booleanValue());
 208         }
 209     }
 210 
 211     /**
 212      * Creates and return an instance of JButton that can be used to
 213      * collapse the right component in the metal split pane.
 214      */
 215     protected JButton createRightOneTouchButton() {
 216         JButton b = new JButton() {
 217             // Sprite buffer for the arrow image of the right button
 218             int[][]     buffer = {{2, 2, 2, 2, 2, 2, 2, 2},
 219                                   {0, 1, 1, 1, 1, 1, 1, 3},
 220                                   {0, 0, 1, 1, 1, 1, 3, 0},
 221                                   {0, 0, 0, 1, 1, 3, 0, 0},
 222                                   {0, 0, 0, 0, 3, 0, 0, 0}};
 223 
 224             public void setBorder(Border border) {
 225             }
 226 
 227             public void paint(Graphics g) {
 228                 JSplitPane splitPane = getSplitPaneFromSuper();
 229                 if(splitPane != null) {
 230                     int         oneTouchSize = getOneTouchSizeFromSuper();
 231                     int         orientation = getOrientationFromSuper();
 232                     int         blockSize = Math.min(getDividerSize(),
 233                                                      oneTouchSize);
 234 
 235                     // Initialize the color array
 236                     Color[]     colors = {
 237                             this.getBackground(),
 238                             MetalLookAndFeel.getPrimaryControlDarkShadow(),
 239                             MetalLookAndFeel.getPrimaryControlInfo(),
 240                             MetalLookAndFeel.getPrimaryControlHighlight()};
 241 
 242                     // Fill the background first ...
 243                     g.setColor(this.getBackground());
 244                     if (isOpaque()) {
 245                         g.fillRect(0, 0, this.getWidth(),
 246                                    this.getHeight());
 247                     }
 248 
 249                     // ... then draw the arrow.
 250                     if (getModel().isPressed()) {
 251                             // Adjust color mapping for pressed button state
 252                             colors[1] = colors[2];
 253                     }
 254                     if(orientation == JSplitPane.VERTICAL_SPLIT) {
 255                             // Draw the image for a vertical split
 256                             for (int i=1; i<=buffer[0].length; i++) {
 257                                     for (int j=1; j<blockSize; j++) {
 258                                             if (buffer[j-1][i-1] == 0) {
 259                                                     continue;
 260                                             }
 261                                             else {
 262                                                 g.setColor(
 263                                                     colors[buffer[j-1][i-1]]);
 264                                             }
 265                                             g.drawLine(i, j, i, j);
 266                                     }
 267                             }
 268                     }
 269                     else {
 270                             // Draw the image for a horizontal split
 271                             // by simply swaping the i and j axis.
 272                             // Except the drawLine() call this code is
 273                             // identical to the code block above. This was done
 274                             // in order to remove the additional orientation
 275                             // check for each pixel.
 276                             for (int i=1; i<=buffer[0].length; i++) {
 277                                     for (int j=1; j<blockSize; j++) {
 278                                             if (buffer[j-1][i-1] == 0) {
 279                                                     // Nothing needs
 280                                                     // to be drawn
 281                                                     continue;
 282                                             }
 283                                             else {
 284                                                     // Set the color from the
 285                                                     // color map
 286                                                     g.setColor(
 287                                                     colors[buffer[j-1][i-1]]);
 288                                             }
 289                                             // Draw a pixel
 290                                             g.drawLine(j, i, j, i);
 291                                     }
 292                             }
 293                     }
 294                 }
 295             }
 296 
 297             // Don't want the button to participate in focus traversable.
 298             @SuppressWarnings("deprecation")
 299             public boolean isFocusTraversable() {
 300                 return false;
 301             }
 302         };
 303         b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 304         b.setFocusPainted(false);
 305         b.setBorderPainted(false);
 306         b.setRequestFocusEnabled(false);
 307         maybeMakeButtonOpaque(b);
 308         return b;
 309     }
 310 
 311     /**
 312      * Used to layout a MetalSplitPaneDivider. Layout for the divider
 313      * involves appropriately moving the left/right buttons around.
 314      * <p>
 315      * This class should be treated as a &quot;protected&quot; inner class.
 316      * Instantiate it only within subclasses of MetalSplitPaneDivider.
 317      */
 318     public class MetalDividerLayout implements LayoutManager {
 319 
 320         // NOTE NOTE NOTE NOTE NOTE
 321         // This class is no longer used, the functionality has
 322         // been rolled into BasicSplitPaneDivider.DividerLayout as a
 323         // defaults property
 324 
 325         public void layoutContainer(Container c) {
 326             JButton     leftButton = getLeftButtonFromSuper();
 327             JButton     rightButton = getRightButtonFromSuper();
 328             JSplitPane  splitPane = getSplitPaneFromSuper();
 329             int         orientation = getOrientationFromSuper();
 330             int         oneTouchSize = getOneTouchSizeFromSuper();
 331             int         oneTouchOffset = getOneTouchOffsetFromSuper();
 332             Insets      insets = getInsets();
 333 
 334             // This layout differs from the one used in BasicSplitPaneDivider.
 335             // It does not center justify the oneTouchExpadable buttons.
 336             // This was necessary in order to meet the spec of the Metal
 337             // splitpane divider.
 338             if (leftButton != null && rightButton != null &&
 339                 c == MetalSplitPaneDivider.this) {
 340                 if (splitPane.isOneTouchExpandable()) {
 341                     if (orientation == JSplitPane.VERTICAL_SPLIT) {
 342                         int extraY = (insets != null) ? insets.top : 0;
 343                         int blockSize = getDividerSize();
 344 
 345                         if (insets != null) {
 346                             blockSize -= (insets.top + insets.bottom);
 347                         }
 348                         blockSize = Math.min(blockSize, oneTouchSize);
 349                         leftButton.setBounds(oneTouchOffset, extraY,
 350                                              blockSize * 2, blockSize);
 351                         rightButton.setBounds(oneTouchOffset +
 352                                               oneTouchSize * 2, extraY,
 353                                               blockSize * 2, blockSize);
 354                     }
 355                     else {
 356                         int blockSize = getDividerSize();
 357                         int extraX = (insets != null) ? insets.left : 0;
 358 
 359                         if (insets != null) {
 360                             blockSize -= (insets.left + insets.right);
 361                         }
 362                         blockSize = Math.min(blockSize, oneTouchSize);
 363                         leftButton.setBounds(extraX, oneTouchOffset,
 364                                              blockSize, blockSize * 2);
 365                         rightButton.setBounds(extraX, oneTouchOffset +
 366                                               oneTouchSize * 2, blockSize,
 367                                               blockSize * 2);
 368                     }
 369                 }
 370                 else {
 371                     leftButton.setBounds(-5, -5, 1, 1);
 372                     rightButton.setBounds(-5, -5, 1, 1);
 373                 }
 374             }
 375         }
 376 
 377         public Dimension minimumLayoutSize(Container c) {
 378             return new Dimension(0,0);
 379         }
 380 
 381         public Dimension preferredLayoutSize(Container c) {
 382             return new Dimension(0, 0);
 383         }
 384 
 385         public void removeLayoutComponent(Component c) {}
 386 
 387         public void addLayoutComponent(String string, Component c) {}
 388     }
 389 
 390     /*
 391      * The following methods only exist in order to be able to access protected
 392      * members in the superclass, because these are otherwise not available
 393      * in any inner class.
 394      */
 395 
 396     int getOneTouchSizeFromSuper() {
 397         return BasicSplitPaneDivider.ONE_TOUCH_SIZE;
 398     }
 399 
 400     int getOneTouchOffsetFromSuper() {
 401         return BasicSplitPaneDivider.ONE_TOUCH_OFFSET;
 402     }
 403 
 404     int getOrientationFromSuper() {
 405         return super.orientation;
 406     }
 407 
 408     JSplitPane getSplitPaneFromSuper() {
 409         return super.splitPane;
 410     }
 411 
 412     JButton getLeftButtonFromSuper() {
 413         return super.leftButton;
 414     }
 415 
 416     JButton getRightButtonFromSuper() {
 417         return super.rightButton;
 418     }
 419 }