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