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