1 /*
   2  * Copyright (c) 1997, 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.sun.java.swing.plaf.motif;
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import javax.swing.JSplitPane;
  31 import javax.swing.UIManager;
  32 import javax.swing.plaf.basic.BasicSplitPaneUI;
  33 import javax.swing.plaf.basic.BasicSplitPaneDivider;
  34 
  35 
  36 /**
  37  * Divider used for Motif split pane.
  38  * <p>
  39  * <strong>Warning:</strong>
  40  * Serialized objects of this class will not be compatible with
  41  * future Swing releases.  The current serialization support is appropriate
  42  * for short term storage or RMI between applications running the same
  43  * version of Swing.  A future release of Swing will provide support for
  44  * long term persistence.
  45  *
  46  * @author Jeff Dinkins
  47  */
  48 @SuppressWarnings("serial") // Same-version serialization only
  49 public class MotifSplitPaneDivider extends BasicSplitPaneDivider
  50 {
  51     /**
  52      * Default cursor, supers is package private, so we have to have one
  53      * too.
  54      */
  55     private static final Cursor defaultCursor =
  56                             Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
  57 
  58 
  59     public static final int minimumThumbSize = 6;
  60     public static final int defaultDividerSize = 18;
  61 
  62     protected  static final int pad = 6;
  63 
  64     private int hThumbOffset = 30;
  65     private int vThumbOffset = 40;
  66     protected int hThumbWidth = 12;
  67     protected int hThumbHeight = 18;
  68     protected int vThumbWidth = 18;
  69     protected int vThumbHeight = 12;
  70 
  71     protected Color highlightColor;
  72     protected Color shadowColor;
  73     protected Color focusedColor;
  74 
  75     /**
  76      * Creates a new Motif SplitPaneDivider
  77      */
  78     public MotifSplitPaneDivider(BasicSplitPaneUI ui) {
  79         super(ui);
  80         highlightColor = UIManager.getColor("SplitPane.highlight");
  81         shadowColor = UIManager.getColor("SplitPane.shadow");
  82         focusedColor = UIManager.getColor("SplitPane.activeThumb");
  83         setDividerSize(hThumbWidth + pad);
  84     }
  85 
  86     /**
  87      * overrides to hardcode the size of the divider
  88      * PENDING(jeff) - rewrite JSplitPane so that this ins't needed
  89      */
  90     public void setDividerSize(int newSize) {
  91         Insets          insets = getInsets();
  92         int             borderSize = 0;
  93         if (getBasicSplitPaneUI().getOrientation() ==
  94             JSplitPane.HORIZONTAL_SPLIT) {
  95             if (insets != null) {
  96                 borderSize = insets.left + insets.right;
  97             }
  98         }
  99         else if (insets != null) {
 100             borderSize = insets.top + insets.bottom;
 101         }
 102         if (newSize < pad + minimumThumbSize + borderSize) {
 103             setDividerSize(pad + minimumThumbSize + borderSize);
 104         } else {
 105             vThumbHeight = hThumbWidth = newSize - pad - borderSize;
 106             super.setDividerSize(newSize);
 107         }
 108     }
 109 
 110     /**
 111       * Paints the divider.
 112       */
 113     // PENDING(jeff) - the thumb's location and size is currently hard coded.
 114     // It should be dynamic.
 115     public void paint(Graphics g) {
 116         Color               bgColor = getBackground();
 117         Dimension           size = getSize();
 118 
 119         // fill
 120         g.setColor(getBackground());
 121         g.fillRect(0, 0, size.width, size.height);
 122 
 123         if(getBasicSplitPaneUI().getOrientation() ==
 124            JSplitPane.HORIZONTAL_SPLIT) {
 125             int center = size.width/2;
 126             int x = center - hThumbWidth/2;
 127             int y = hThumbOffset;
 128 
 129             // split line
 130             g.setColor(shadowColor);
 131             g.drawLine(center-1, 0, center-1, size.height);
 132 
 133             g.setColor(highlightColor);
 134             g.drawLine(center, 0, center, size.height);
 135 
 136             // draw thumb
 137             g.setColor((splitPane.hasFocus()) ? focusedColor :
 138                                                 getBackground());
 139             g.fillRect(x+1, y+1, hThumbWidth-2, hThumbHeight-1);
 140 
 141             g.setColor(highlightColor);
 142             g.drawLine(x, y, x+hThumbWidth-1, y);       // top
 143             g.drawLine(x, y+1, x, y+hThumbHeight-1);    // left
 144 
 145             g.setColor(shadowColor);
 146             g.drawLine(x+1, y+hThumbHeight-1,
 147                        x+hThumbWidth-1, y+hThumbHeight-1);      // bottom
 148             g.drawLine(x+hThumbWidth-1, y+1,
 149                        x+hThumbWidth-1, y+hThumbHeight-2);      // right
 150 
 151         } else {
 152             int center = size.height/2;
 153             int x = size.width - vThumbOffset;
 154             int y = size.height/2 - vThumbHeight/2;
 155 
 156             // split line
 157             g.setColor(shadowColor);
 158             g.drawLine(0, center-1, size.width, center-1);
 159 
 160             g.setColor(highlightColor);
 161             g.drawLine(0, center, size.width, center);
 162 
 163             // draw thumb
 164             g.setColor((splitPane.hasFocus()) ? focusedColor :
 165                                                 getBackground());
 166             g.fillRect(x+1, y+1, vThumbWidth-1, vThumbHeight-1);
 167 
 168             g.setColor(highlightColor);
 169             g.drawLine(x, y, x+vThumbWidth, y);    // top
 170             g.drawLine(x, y+1, x, y+vThumbHeight); // left
 171 
 172             g.setColor(shadowColor);
 173             g.drawLine(x+1, y+vThumbHeight,
 174                        x+vThumbWidth, y+vThumbHeight);          // bottom
 175             g.drawLine(x+vThumbWidth, y+1,
 176                        x+vThumbWidth, y+vThumbHeight-1);        // right
 177         }
 178         super.paint(g);
 179 
 180     }
 181 
 182     /**
 183       * The minimums size is the same as the preferredSize
 184       */
 185     public Dimension getMinimumSize() {
 186         return getPreferredSize();
 187     }
 188 
 189     /**
 190      * Sets the SplitPaneUI that is using the receiver. This is completely
 191      * overriden from super to create a different MouseHandler.
 192      */
 193     public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) {
 194         if (splitPane != null) {
 195             splitPane.removePropertyChangeListener(this);
 196            if (mouseHandler != null) {
 197                splitPane.removeMouseListener(mouseHandler);
 198                splitPane.removeMouseMotionListener(mouseHandler);
 199                removeMouseListener(mouseHandler);
 200                removeMouseMotionListener(mouseHandler);
 201                mouseHandler = null;
 202            }
 203         }
 204         splitPaneUI = newUI;
 205         if (newUI != null) {
 206             splitPane = newUI.getSplitPane();
 207             if (splitPane != null) {
 208                 if (mouseHandler == null) mouseHandler=new MotifMouseHandler();
 209                 splitPane.addMouseListener(mouseHandler);
 210                 splitPane.addMouseMotionListener(mouseHandler);
 211                 addMouseListener(mouseHandler);
 212                 addMouseMotionListener(mouseHandler);
 213                 splitPane.addPropertyChangeListener(this);
 214                 if (splitPane.isOneTouchExpandable()) {
 215                     oneTouchExpandableChanged();
 216                 }
 217             }
 218         }
 219         else {
 220             splitPane = null;
 221         }
 222     }
 223 
 224     /**
 225      * Returns true if the point at <code>x</code>, <code>y</code>
 226      * is inside the thumb.
 227      */
 228     private boolean isInThumb(int x, int y) {
 229         Dimension           size = getSize();
 230         int                 thumbX;
 231         int                 thumbY;
 232         int                 thumbWidth;
 233         int                 thumbHeight;
 234 
 235         if (getBasicSplitPaneUI().getOrientation() ==
 236             JSplitPane.HORIZONTAL_SPLIT) {
 237             int center = size.width/2;
 238             thumbX = center - hThumbWidth/2;
 239             thumbY = hThumbOffset;
 240             thumbWidth = hThumbWidth;
 241             thumbHeight = hThumbHeight;
 242         }
 243         else {
 244             int center = size.height/2;
 245             thumbX = size.width - vThumbOffset;
 246             thumbY = size.height/2 - vThumbHeight/2;
 247             thumbWidth = vThumbWidth;
 248             thumbHeight = vThumbHeight;
 249         }
 250         return (x >= thumbX && x < (thumbX + thumbWidth) &&
 251                 y >= thumbY && y < (thumbY + thumbHeight));
 252     }
 253 
 254     //
 255     // Two methods are exposed so that MotifMouseHandler can see the
 256     // superclass protected ivars
 257     //
 258 
 259     private DragController getDragger() {
 260         return dragger;
 261     }
 262 
 263     private JSplitPane getSplitPane() {
 264         return splitPane;
 265     }
 266 
 267 
 268     /**
 269      * MouseHandler is subclassed to only pass off to super if the mouse
 270      * is in the thumb. Motif only allows dragging when the thumb is clicked
 271      * in.
 272      */
 273     private class MotifMouseHandler extends MouseHandler {
 274         public void mousePressed(MouseEvent e) {
 275             // Constrain the mouse pressed to the thumb.
 276             if (e.getSource() == MotifSplitPaneDivider.this &&
 277                 getDragger() == null && getSplitPane().isEnabled() &&
 278                 isInThumb(e.getX(), e.getY())) {
 279                 super.mousePressed(e);
 280             }
 281         }
 282 
 283         public void mouseMoved(MouseEvent e) {
 284             if (getDragger() != null) {
 285                 return;
 286             }
 287             if (!isInThumb(e.getX(), e.getY())) {
 288                 if (getCursor() != defaultCursor) {
 289                     setCursor(defaultCursor);
 290                 }
 291                 return;
 292             }
 293             super.mouseMoved(e);
 294         }
 295     }
 296 }