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