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             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 &quot;protected&quot; 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 }