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