1 /* 2 * Copyright (c) 2002, 2010, 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 import javax.swing.*; 29 import javax.swing.text.JTextComponent; 30 import javax.swing.border.*; 31 import javax.swing.plaf.*; 32 import javax.swing.plaf.basic.*; 33 34 import java.beans.PropertyChangeListener; 35 import java.beans.PropertyChangeEvent; 36 37 import java.awt.*; 38 import java.awt.event.ContainerListener; 39 import java.awt.event.ContainerEvent; 40 import java.awt.event.FocusListener; 41 import java.awt.event.FocusEvent; 42 43 /** 44 * Provides the Synth L&F UI delegate for 45 * {@link javax.swing.JScrollPane}. 46 * 47 * @author Scott Violet 48 * @since 1.7 49 */ 50 public class SynthScrollPaneUI extends BasicScrollPaneUI 51 implements PropertyChangeListener, SynthUI { 52 private SynthStyle style; 53 private boolean viewportViewHasFocus = false; 54 private ViewportViewFocusHandler viewportViewFocusHandler; 55 56 /** 57 * Creates a new UI object for the given component. 58 * 59 * @param x component to create UI object for 60 * @return the UI object 61 */ 62 public static ComponentUI createUI(JComponent x) { 63 return new SynthScrollPaneUI(); 64 } 65 66 /** 67 * Notifies this UI delegate to repaint the specified component. 68 * This method paints the component background, then calls 69 * the {@link #paint(SynthContext,Graphics)} method. 70 * 71 * <p>In general, this method does not need to be overridden by subclasses. 72 * All Look and Feel rendering code should reside in the {@code paint} method. 73 * 74 * @param g the {@code Graphics} object used for painting 75 * @param c the component being painted 76 * @see #paint(SynthContext,Graphics) 77 */ 78 @Override 79 public void update(Graphics g, JComponent c) { 80 SynthContext context = getContext(c); 81 82 SynthLookAndFeel.update(context, g); 83 context.getPainter().paintScrollPaneBackground(context, 84 g, 0, 0, c.getWidth(), c.getHeight()); 85 paint(context, g); 86 context.dispose(); 87 } 88 89 /** 90 * Paints the specified component according to the Look and Feel. 91 * <p>This method is not used by Synth Look and Feel. 92 * Painting is handled by the {@link #paint(SynthContext,Graphics)} method. 93 * 94 * @param g the {@code Graphics} object used for painting 95 * @param c the component being painted 96 * @see #paint(SynthContext,Graphics) 97 */ 98 @Override 99 public void paint(Graphics g, JComponent c) { 100 SynthContext context = getContext(c); 101 102 paint(context, g); 103 context.dispose(); 104 } 105 106 /** 107 * Paints the specified component. 108 * 109 * @param context context for the component being painted 110 * @param g the {@code Graphics} object used for painting 111 * @see #update(Graphics,JComponent) 112 */ 113 protected void paint(SynthContext context, Graphics g) { 114 Border vpBorder = scrollpane.getViewportBorder(); 115 if (vpBorder != null) { 116 Rectangle r = scrollpane.getViewportBorderBounds(); 117 vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height); 118 } 119 } 120 121 /** 122 * @inheritDoc 123 */ 124 @Override 125 public void paintBorder(SynthContext context, Graphics g, int x, 126 int y, int w, int h) { 127 context.getPainter().paintScrollPaneBorder(context, g, x, y, w, h); 128 } 129 130 /** 131 * @inheritDoc 132 */ 133 @Override 134 protected void installDefaults(JScrollPane scrollpane) { 135 updateStyle(scrollpane); 136 } 137 138 private void updateStyle(JScrollPane c) { 139 SynthContext context = getContext(c, ENABLED); 140 SynthStyle oldStyle = style; 141 142 style = SynthLookAndFeel.updateStyle(context, this); 143 if (style != oldStyle) { 144 Border vpBorder = scrollpane.getViewportBorder(); 145 if ((vpBorder == null) ||( vpBorder instanceof UIResource)) { 146 scrollpane.setViewportBorder(new ViewportBorder(context)); 147 } 148 if (oldStyle != null) { 149 uninstallKeyboardActions(c); 150 installKeyboardActions(c); 151 } 152 } 153 context.dispose(); 154 } 155 156 /** 157 * @inheritDoc 158 */ 159 @Override 160 protected void installListeners(JScrollPane c) { 161 super.installListeners(c); 162 c.addPropertyChangeListener(this); 163 if (UIManager.getBoolean("ScrollPane.useChildTextComponentFocus")){ 164 viewportViewFocusHandler = new ViewportViewFocusHandler(); 165 c.getViewport().addContainerListener(viewportViewFocusHandler); 166 Component view = c.getViewport().getView(); 167 if (view instanceof JTextComponent) { 168 view.addFocusListener(viewportViewFocusHandler); 169 } 170 } 171 } 172 173 /** 174 * @inheritDoc 175 */ 176 @Override 177 protected void uninstallDefaults(JScrollPane c) { 178 SynthContext context = getContext(c, ENABLED); 179 180 style.uninstallDefaults(context); 181 context.dispose(); 182 183 if (scrollpane.getViewportBorder() instanceof UIResource) { 184 scrollpane.setViewportBorder(null); 185 } 186 } 187 188 /** 189 * @inheritDoc 190 */ 191 @Override 192 protected void uninstallListeners(JComponent c) { 193 super.uninstallListeners(c); 194 c.removePropertyChangeListener(this); 195 if (viewportViewFocusHandler != null) { 196 JViewport viewport = ((JScrollPane) c).getViewport(); 197 viewport.removeContainerListener(viewportViewFocusHandler); 198 if (viewport.getView()!= null) { 199 viewport.getView().removeFocusListener(viewportViewFocusHandler); 200 } 201 viewportViewFocusHandler = null; 202 } 203 } 204 205 /** 206 * @inheritDoc 207 */ 208 @Override 209 public SynthContext getContext(JComponent c) { 210 return getContext(c, getComponentState(c)); 211 } 212 213 private SynthContext getContext(JComponent c, int state) { 214 return SynthContext.getContext(SynthContext.class, c, 215 SynthLookAndFeel.getRegion(c), style, state); 216 } 217 218 private int getComponentState(JComponent c) { 219 int baseState = SynthLookAndFeel.getComponentState(c); 220 if (viewportViewFocusHandler!=null && viewportViewHasFocus){ 221 baseState = baseState | FOCUSED; 222 } 223 return baseState; 224 } 225 226 public void propertyChange(PropertyChangeEvent e) { 227 if (SynthLookAndFeel.shouldUpdateStyle(e)) { 228 updateStyle(scrollpane); 229 } 230 } 231 232 233 234 private class ViewportBorder extends AbstractBorder implements UIResource { 235 private Insets insets; 236 237 ViewportBorder(SynthContext context) { 238 this.insets = (Insets)context.getStyle().get(context, 239 "ScrollPane.viewportBorderInsets"); 240 if (this.insets == null) { 241 this.insets = SynthLookAndFeel.EMPTY_UIRESOURCE_INSETS; 242 } 243 } 244 245 @Override 246 public void paintBorder(Component c, Graphics g, int x, int y, 247 int width, int height) { 248 JComponent jc = (JComponent)c; 249 SynthContext context = getContext(jc); 250 SynthStyle style = context.getStyle(); 251 if (style == null) { 252 assert false: "SynthBorder is being used outside after the " + 253 " UI has been uninstalled"; 254 return; 255 } 256 context.getPainter().paintViewportBorder(context, g, x, y, width, 257 height); 258 context.dispose(); 259 } 260 261 @Override 262 public Insets getBorderInsets(Component c, Insets insets) { 263 if (insets == null) { 264 return new Insets(this.insets.top, this.insets.left, 265 this.insets.bottom, this.insets.right); 266 } 267 insets.top = this.insets.top; 268 insets.bottom = this.insets.bottom; 269 insets.left = this.insets.left; 270 insets.right = this.insets.left; 271 return insets; 272 } 273 274 @Override 275 public boolean isBorderOpaque() { 276 return false; 277 } 278 } 279 280 /** 281 * Handle keeping track of the viewport's view's focus 282 */ 283 private class ViewportViewFocusHandler implements ContainerListener, 284 FocusListener{ 285 public void componentAdded(ContainerEvent e) { 286 if (e.getChild() instanceof JTextComponent) { 287 e.getChild().addFocusListener(this); 288 viewportViewHasFocus = e.getChild().isFocusOwner(); 289 scrollpane.repaint(); 290 } 291 } 292 293 public void componentRemoved(ContainerEvent e) { 294 if (e.getChild() instanceof JTextComponent) { 295 e.getChild().removeFocusListener(this); 296 } 297 } 298 299 public void focusGained(FocusEvent e) { 300 viewportViewHasFocus = true; 301 scrollpane.repaint(); 302 } 303 304 public void focusLost(FocusEvent e) { 305 viewportViewHasFocus = false; 306 scrollpane.repaint(); 307 } 308 } 309 }