1 /* 2 * Copyright (c) 2002, 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 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 context.dispose(); 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 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 context.dispose(); 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 context.dispose(); 183 style = null; 184 185 context = getContext(splitPane, Region.SPLIT_PANE_DIVIDER, ENABLED); 186 dividerStyle.uninstallDefaults(context); 187 context.dispose(); 188 dividerStyle = null; 189 190 super.uninstallDefaults(); 191 } 192 193 194 /** 195 * Uninstalls the event listeners from the UI. 196 */ 197 @Override 198 protected void uninstallListeners() { 199 super.uninstallListeners(); 200 splitPane.removePropertyChangeListener(this); 201 } 202 203 /** 204 * {@inheritDoc} 205 */ 206 @Override 207 public SynthContext getContext(JComponent c) { 208 return getContext(c, SynthLookAndFeel.getComponentState(c)); 209 } 210 211 private SynthContext getContext(JComponent c, int state) { 212 return SynthContext.getContext(SynthContext.class, c, 213 SynthLookAndFeel.getRegion(c), style, state); 214 } 215 216 SynthContext getContext(JComponent c, Region region) { 217 return getContext(c, region, getComponentState(c, region)); 218 } 219 220 private SynthContext getContext(JComponent c, Region region, int state) { 221 if (region == Region.SPLIT_PANE_DIVIDER) { 222 return SynthContext.getContext(SynthContext.class, c, region, 223 dividerStyle, state); 224 } 225 return SynthContext.getContext(SynthContext.class, c, region, 226 style, state); 227 } 228 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 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 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 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 context.dispose(); 293 } 294 295 /** 296 * Paints the specified component according to the Look and Feel. 297 * <p>This method is not used by Synth Look and Feel. 298 * Painting is handled by the {@link #paint(SynthContext,Graphics)} method. 299 * 300 * @param g the {@code Graphics} object used for painting 301 * @param c the component being painted 302 * @see #paint(SynthContext,Graphics) 303 */ 304 @Override 305 public void paint(Graphics g, JComponent c) { 306 SynthContext context = getContext(c); 307 308 paint(context, g); 309 context.dispose(); 310 } 311 312 /** 313 * Paints the specified component. This implementation does nothing. 314 * 315 * @param context context for the component being painted 316 * @param g the {@code Graphics} object used for painting 317 * @see #update(Graphics,JComponent) 318 */ 319 protected void paint(SynthContext context, Graphics g) { 320 // This is done to update package private variables in 321 // BasicSplitPaneUI 322 super.paint(g, splitPane); 323 } 324 325 /** 326 * {@inheritDoc} 327 */ 328 @Override 329 public void paintBorder(SynthContext context, Graphics g, int x, 330 int y, int w, int h) { 331 context.getPainter().paintSplitPaneBorder(context, g, x, y, w, h); 332 } 333 334 private void paintDragDivider(Graphics g, int x, int y, int w, int h) { 335 SynthContext context = getContext(splitPane,Region.SPLIT_PANE_DIVIDER); 336 context.setComponentState(((context.getComponentState() | MOUSE_OVER) ^ 337 MOUSE_OVER) | PRESSED); 338 Shape oldClip = g.getClip(); 339 g.clipRect(x, y, w, h); 340 context.getPainter().paintSplitPaneDragDivider(context, g, x, y, w, h, 341 splitPane.getOrientation()); 342 g.setClip(oldClip); 343 context.dispose(); 344 } 345 346 /** 347 * {@inheritDoc} 348 */ 349 @Override 350 public void finishedPaintingChildren(JSplitPane jc, Graphics g) { 351 if(jc == splitPane && getLastDragLocation() != -1 && 352 !isContinuousLayout() && !draggingHW) { 353 if(jc.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { 354 paintDragDivider(g, getLastDragLocation(), 0, dividerSize - 1, 355 splitPane.getHeight() - 1); 356 } else { 357 paintDragDivider(g, 0, getLastDragLocation(), 358 splitPane.getWidth() - 1, dividerSize - 1); 359 } 360 } 361 } 362 }