1 /* 2 * Copyright (c) 2002, 2017, 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.synth; 27 28 29 import java.awt.*; 30 import java.awt.event.*; 31 import java.beans.*; 32 import java.util.*; 33 import javax.swing.*; 34 import javax.swing.plaf.*; 35 import javax.swing.plaf.basic.*; 36 37 38 /** 39 * Provides the Synth L&F UI delegate for 40 * {@link javax.swing.JSplitPane}. 41 * 42 * @author Scott Violet 43 * @since 1.7 44 */ 45 public class SynthSplitPaneUI extends BasicSplitPaneUI 46 implements PropertyChangeListener, SynthUI { 47 /** 48 * Keys to use for forward focus traversal when the JComponent is 49 * managing focus. 50 */ 51 private Set<KeyStroke> managingFocusForwardTraversalKeys; 52 53 /** 54 * Keys to use for backward focus traversal when the JComponent is 55 * managing focus. 56 */ 57 private Set<KeyStroke> managingFocusBackwardTraversalKeys; 58 59 /** 60 * Style for the JSplitPane. 61 */ 62 private SynthStyle style; 63 /** 64 * Style for the divider. 65 */ 66 private SynthStyle dividerStyle; 67 68 /** 69 * 70 * Constructs a {@code SynthSplitPaneUI}. 71 */ SynthSplitPaneUI()72 public SynthSplitPaneUI() {} 73 74 /** 75 * Creates a new SynthSplitPaneUI instance 76 * 77 * @param x component to create UI object for 78 * @return the UI object 79 */ createUI(JComponent x)80 public static ComponentUI createUI(JComponent x) { 81 return new SynthSplitPaneUI(); 82 } 83 84 /** 85 * Installs the UI defaults. 86 */ 87 @Override 88 @SuppressWarnings("deprecation") installDefaults()89 protected void installDefaults() { 90 updateStyle(splitPane); 91 92 setOrientation(splitPane.getOrientation()); 93 setContinuousLayout(splitPane.isContinuousLayout()); 94 95 resetLayoutManager(); 96 97 /* Install the nonContinuousLayoutDivider here to avoid having to 98 add/remove everything later. */ 99 if(nonContinuousLayoutDivider == null) { 100 setNonContinuousLayoutDivider( 101 createDefaultNonContinuousLayoutDivider(), 102 true); 103 } else { 104 setNonContinuousLayoutDivider(nonContinuousLayoutDivider, true); 105 } 106 107 // focus forward traversal key 108 if (managingFocusForwardTraversalKeys==null) { 109 managingFocusForwardTraversalKeys = new HashSet<KeyStroke>(); 110 managingFocusForwardTraversalKeys.add( 111 KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)); 112 } 113 splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, 114 managingFocusForwardTraversalKeys); 115 // focus backward traversal key 116 if (managingFocusBackwardTraversalKeys==null) { 117 managingFocusBackwardTraversalKeys = new HashSet<KeyStroke>(); 118 managingFocusBackwardTraversalKeys.add( 119 KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK)); 120 } 121 splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, 122 managingFocusBackwardTraversalKeys); 123 } 124 updateStyle(JSplitPane splitPane)125 private void updateStyle(JSplitPane splitPane) { 126 SynthContext context = getContext(splitPane, Region.SPLIT_PANE_DIVIDER, 127 ENABLED); 128 SynthStyle oldDividerStyle = dividerStyle; 129 dividerStyle = SynthLookAndFeel.updateStyle(context, this); 130 131 context = getContext(splitPane, ENABLED); 132 SynthStyle oldStyle = style; 133 134 style = SynthLookAndFeel.updateStyle(context, this); 135 136 if (style != oldStyle) { 137 Object value = style.get(context, "SplitPane.size"); 138 if (value == null) { 139 value = Integer.valueOf(6); 140 } 141 LookAndFeel.installProperty(splitPane, "dividerSize", value); 142 dividerSize = ((Number)value).intValue(); 143 144 value = style.get(context, "SplitPane.oneTouchExpandable"); 145 if (value != null) { 146 LookAndFeel.installProperty(splitPane, "oneTouchExpandable", value); 147 } 148 149 if (divider != null) { 150 splitPane.remove(divider); 151 divider.setDividerSize(splitPane.getDividerSize()); 152 } 153 if (oldStyle != null) { 154 uninstallKeyboardActions(); 155 installKeyboardActions(); 156 } 157 } 158 if (style != oldStyle || dividerStyle != oldDividerStyle) { 159 // Only way to force BasicSplitPaneDivider to reread the 160 // necessary properties. 161 if (divider != null) { 162 splitPane.remove(divider); 163 } 164 divider = createDefaultDivider(); 165 divider.setBasicSplitPaneUI(this); 166 splitPane.add(divider, JSplitPane.DIVIDER); 167 } 168 } 169 170 /** 171 * Installs the event listeners for the UI. 172 */ 173 @Override installListeners()174 protected void installListeners() { 175 super.installListeners(); 176 splitPane.addPropertyChangeListener(this); 177 } 178 179 /** 180 * Uninstalls the UI defaults. 181 */ 182 @Override uninstallDefaults()183 protected void uninstallDefaults() { 184 SynthContext context = getContext(splitPane, ENABLED); 185 186 style.uninstallDefaults(context); 187 style = null; 188 189 context = getContext(splitPane, Region.SPLIT_PANE_DIVIDER, ENABLED); 190 dividerStyle.uninstallDefaults(context); 191 dividerStyle = null; 192 193 super.uninstallDefaults(); 194 } 195 196 197 /** 198 * Uninstalls the event listeners from the UI. 199 */ 200 @Override uninstallListeners()201 protected void uninstallListeners() { 202 super.uninstallListeners(); 203 splitPane.removePropertyChangeListener(this); 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override getContext(JComponent c)210 public SynthContext getContext(JComponent c) { 211 return getContext(c, SynthLookAndFeel.getComponentState(c)); 212 } 213 getContext(JComponent c, int state)214 private SynthContext getContext(JComponent c, int state) { 215 return SynthContext.getContext(c, style, state); 216 } 217 getContext(JComponent c, Region region)218 SynthContext getContext(JComponent c, Region region) { 219 return getContext(c, region, getComponentState(c, region)); 220 } 221 getContext(JComponent c, Region region, int state)222 private SynthContext getContext(JComponent c, Region region, int state) { 223 if (region == Region.SPLIT_PANE_DIVIDER) { 224 return SynthContext.getContext(c, region, dividerStyle, state); 225 } 226 return SynthContext.getContext(c, region, style, state); 227 } 228 getComponentState(JComponent c, Region subregion)229 private int getComponentState(JComponent c, Region subregion) { 230 int state = SynthLookAndFeel.getComponentState(c); 231 232 if (divider.isMouseOver()) { 233 state |= MOUSE_OVER; 234 } 235 return state; 236 } 237 238 /** 239 * {@inheritDoc} 240 */ 241 @Override propertyChange(PropertyChangeEvent e)242 public void propertyChange(PropertyChangeEvent e) { 243 if (SynthLookAndFeel.shouldUpdateStyle(e)) { 244 updateStyle((JSplitPane)e.getSource()); 245 } 246 } 247 248 /** 249 * Creates the default divider. 250 */ 251 @Override createDefaultDivider()252 public BasicSplitPaneDivider createDefaultDivider() { 253 SynthSplitPaneDivider divider = new SynthSplitPaneDivider(this); 254 255 divider.setDividerSize(splitPane.getDividerSize()); 256 return divider; 257 } 258 259 /** 260 * {@inheritDoc} 261 */ 262 @Override 263 @SuppressWarnings("serial") // anonymous class createDefaultNonContinuousLayoutDivider()264 protected Component createDefaultNonContinuousLayoutDivider() { 265 return new Canvas() { 266 public void paint(Graphics g) { 267 paintDragDivider(g, 0, 0, getWidth(), getHeight()); 268 } 269 }; 270 } 271 272 /** 273 * Notifies this UI delegate to repaint the specified component. 274 * This method paints the component background, then calls 275 * the {@link #paint(SynthContext,Graphics)} method. 276 * 277 * <p>In general, this method does not need to be overridden by subclasses. 278 * All Look and Feel rendering code should reside in the {@code paint} method. 279 * 280 * @param g the {@code Graphics} object used for painting 281 * @param c the component being painted 282 * @see #paint(SynthContext,Graphics) 283 */ 284 @Override 285 public void update(Graphics g, JComponent c) { 286 SynthContext context = getContext(c); 287 288 SynthLookAndFeel.update(context, g); 289 context.getPainter().paintSplitPaneBackground(context, 290 g, 0, 0, c.getWidth(), c.getHeight()); 291 paint(context, g); 292 } 293 294 /** 295 * Paints the specified component according to the Look and Feel. 296 * <p>This method is not used by Synth Look and Feel. 297 * Painting is handled by the {@link #paint(SynthContext,Graphics)} method. 298 * 299 * @param g the {@code Graphics} object used for painting 300 * @param c the component being painted 301 * @see #paint(SynthContext,Graphics) 302 */ 303 @Override 304 public void paint(Graphics g, JComponent c) { 305 SynthContext context = getContext(c); 306 307 paint(context, g); 308 } 309 310 /** 311 * Paints the specified component. This implementation does nothing. 312 * 313 * @param context context for the component being painted 314 * @param g the {@code Graphics} object used for painting 315 * @see #update(Graphics,JComponent) 316 */ 317 protected void paint(SynthContext context, Graphics g) { 318 // This is done to update package private variables in 319 // BasicSplitPaneUI 320 super.paint(g, splitPane); 321 } 322 323 /** 324 * {@inheritDoc} 325 */ 326 @Override 327 public void paintBorder(SynthContext context, Graphics g, int x, 328 int y, int w, int h) { 329 context.getPainter().paintSplitPaneBorder(context, g, x, y, w, h); 330 } 331 332 private void paintDragDivider(Graphics g, int x, int y, int w, int h) { 333 SynthContext context = getContext(splitPane,Region.SPLIT_PANE_DIVIDER); 334 context.setComponentState(((context.getComponentState() | MOUSE_OVER) ^ 335 MOUSE_OVER) | PRESSED); 336 Shape oldClip = g.getClip(); 337 g.clipRect(x, y, w, h); 338 context.getPainter().paintSplitPaneDragDivider(context, g, x, y, w, h, 339 splitPane.getOrientation()); 340 g.setClip(oldClip); 341 } 342 343 /** 344 * {@inheritDoc} 345 */ 346 @Override 347 public void finishedPaintingChildren(JSplitPane jc, Graphics g) { 348 if(jc == splitPane && getLastDragLocation() != -1 && 349 !isContinuousLayout() && !draggingHW) { 350 if(jc.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { 351 paintDragDivider(g, getLastDragLocation(), 0, dividerSize - 1, 352 splitPane.getHeight() - 1); 353 } else { 354 paintDragDivider(g, 0, getLastDragLocation(), 355 splitPane.getWidth() - 1, dividerSize - 1); 356 } 357 } 358 } 359 } 360