1 /* 2 * Copyright (c) 1998, 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.metal; 27 28 import javax.swing.*; 29 import java.awt.Color; 30 import java.awt.Component; 31 import java.awt.Container; 32 import java.awt.Dimension; 33 import java.awt.Frame; 34 import java.awt.Graphics; 35 import java.awt.GraphicsEnvironment; 36 import java.awt.Insets; 37 import java.awt.Point; 38 import java.awt.Rectangle; 39 import java.awt.event.*; 40 import java.lang.ref.WeakReference; 41 import java.util.*; 42 43 import java.beans.PropertyChangeListener; 44 45 import javax.swing.event.*; 46 import javax.swing.border.*; 47 import javax.swing.plaf.*; 48 import javax.swing.plaf.basic.*; 49 50 /** 51 * A Metal Look and Feel implementation of ToolBarUI. This implementation 52 * is a "combined" view/controller. 53 * 54 * @author Jeff Shapiro 55 */ 56 public class MetalToolBarUI extends BasicToolBarUI 57 { 58 /** 59 * An array of WeakReferences that point to JComponents. This will contain 60 * instances of JToolBars and JMenuBars and is used to find 61 * JToolBars/JMenuBars that border each other. 62 */ 63 private static List<WeakReference<JComponent>> components = new ArrayList<WeakReference<JComponent>>(); 64 65 /** 66 * This protected field is implementation specific. Do not access directly 67 * or override. Use the create method instead. 68 * 69 * @see #createContainerListener 70 */ 71 protected ContainerListener contListener; 72 73 /** 74 * This protected field is implementation specific. Do not access directly 75 * or override. Use the create method instead. 76 * 77 * @see #createRolloverListener 78 */ 79 protected PropertyChangeListener rolloverListener; 80 81 private static Border nonRolloverBorder; 82 83 /** 84 * Last menubar the toolbar touched. This is only useful for ocean. 85 */ 86 private JMenuBar lastMenuBar; 87 88 /** 89 * Constructs a {@code MetalToolBarUI}. 90 */ MetalToolBarUI()91 public MetalToolBarUI() {} 92 93 /** 94 * Registers the specified component. 95 */ register(JComponent c)96 static synchronized void register(JComponent c) { 97 if (c == null) { 98 // Exception is thrown as convenience for callers that are 99 // typed to throw an NPE. 100 throw new NullPointerException("JComponent must be non-null"); 101 } 102 components.add(new WeakReference<JComponent>(c)); 103 } 104 105 /** 106 * Unregisters the specified component. 107 */ unregister(JComponent c)108 static synchronized void unregister(JComponent c) { 109 for (int counter = components.size() - 1; counter >= 0; counter--) { 110 // Search for the component, removing any flushed references 111 // along the way. 112 JComponent target = components.get(counter).get(); 113 114 if (target == c || target == null) { 115 components.remove(counter); 116 } 117 } 118 } 119 120 /** 121 * Finds a previously registered component of class <code>target</code> 122 * that shares the JRootPane ancestor of <code>from</code>. 123 */ findRegisteredComponentOfType(JComponent from, Class<?> target)124 static synchronized Object findRegisteredComponentOfType(JComponent from, 125 Class<?> target) { 126 JRootPane rp = SwingUtilities.getRootPane(from); 127 if (rp != null) { 128 for (int counter = components.size() - 1; counter >= 0; counter--){ 129 Object component = ((WeakReference)components.get(counter)). 130 get(); 131 132 if (component == null) { 133 // WeakReference has gone away, remove the WeakReference 134 components.remove(counter); 135 } 136 else if (target.isInstance(component) && SwingUtilities. 137 getRootPane((Component)component) == rp) { 138 return component; 139 } 140 } 141 } 142 return null; 143 } 144 145 /** 146 * Returns true if the passed in JMenuBar is above a horizontal 147 * JToolBar. 148 */ doesMenuBarBorderToolBar(JMenuBar c)149 static boolean doesMenuBarBorderToolBar(JMenuBar c) { 150 JToolBar tb = (JToolBar)MetalToolBarUI. 151 findRegisteredComponentOfType(c, JToolBar.class); 152 if (tb != null && tb.getOrientation() == JToolBar.HORIZONTAL) { 153 JRootPane rp = SwingUtilities.getRootPane(c); 154 Point point = new Point(0, 0); 155 point = SwingUtilities.convertPoint(c, point, rp); 156 int menuX = point.x; 157 int menuY = point.y; 158 point.x = point.y = 0; 159 point = SwingUtilities.convertPoint(tb, point, rp); 160 return (point.x == menuX && menuY + c.getHeight() == point.y && 161 c.getWidth() == tb.getWidth()); 162 } 163 return false; 164 } 165 166 /** 167 * Constructs an instance of {@code MetalToolBarUI}. 168 * 169 * @param c a component 170 * @return an instance of {@code MetalToolBarUI} 171 */ createUI( JComponent c )172 public static ComponentUI createUI( JComponent c ) 173 { 174 return new MetalToolBarUI(); 175 } 176 installUI( JComponent c )177 public void installUI( JComponent c ) 178 { 179 super.installUI( c ); 180 register(c); 181 } 182 uninstallUI( JComponent c )183 public void uninstallUI( JComponent c ) 184 { 185 super.uninstallUI( c ); 186 nonRolloverBorder = null; 187 unregister(c); 188 } 189 installListeners()190 protected void installListeners() { 191 super.installListeners(); 192 193 contListener = createContainerListener(); 194 if (contListener != null) { 195 toolBar.addContainerListener(contListener); 196 } 197 rolloverListener = createRolloverListener(); 198 if (rolloverListener != null) { 199 toolBar.addPropertyChangeListener(rolloverListener); 200 } 201 } 202 uninstallListeners()203 protected void uninstallListeners() { 204 super.uninstallListeners(); 205 206 if (contListener != null) { 207 toolBar.removeContainerListener(contListener); 208 } 209 rolloverListener = createRolloverListener(); 210 if (rolloverListener != null) { 211 toolBar.removePropertyChangeListener(rolloverListener); 212 } 213 } 214 createRolloverBorder()215 protected Border createRolloverBorder() { 216 return super.createRolloverBorder(); 217 } 218 createNonRolloverBorder()219 protected Border createNonRolloverBorder() { 220 return super.createNonRolloverBorder(); 221 } 222 223 224 /** 225 * Creates a non rollover border for Toggle buttons in the toolbar. 226 */ createNonRolloverToggleBorder()227 private Border createNonRolloverToggleBorder() { 228 return createNonRolloverBorder(); 229 } 230 setBorderToNonRollover(Component c)231 protected void setBorderToNonRollover(Component c) { 232 if (c instanceof JToggleButton && !(c instanceof JCheckBox)) { 233 // 4735514, 4886944: The method createNonRolloverToggleBorder() is 234 // private in BasicToolBarUI so we can't override it. We still need 235 // to call super from this method so that it can save away the 236 // original border and then we install ours. 237 238 // Before calling super we get a handle to the old border, because 239 // super will install a non-UIResource border that we can't 240 // distinguish from one provided by an application. 241 JToggleButton b = (JToggleButton)c; 242 Border border = b.getBorder(); 243 super.setBorderToNonRollover(c); 244 if (border instanceof UIResource) { 245 if (nonRolloverBorder == null) { 246 nonRolloverBorder = createNonRolloverToggleBorder(); 247 } 248 b.setBorder(nonRolloverBorder); 249 } 250 } else { 251 super.setBorderToNonRollover(c); 252 } 253 } 254 255 256 /** 257 * Creates a container listener that will be added to the JToolBar. 258 * If this method returns null then it will not be added to the 259 * toolbar. 260 * 261 * @return an instance of a <code>ContainerListener</code> or null 262 */ createContainerListener()263 protected ContainerListener createContainerListener() { 264 return null; 265 } 266 267 /** 268 * Creates a property change listener that will be added to the JToolBar. 269 * If this method returns null then it will not be added to the 270 * toolbar. 271 * 272 * @return an instance of a <code>PropertyChangeListener</code> or null 273 */ createRolloverListener()274 protected PropertyChangeListener createRolloverListener() { 275 return null; 276 } 277 createDockingListener( )278 protected MouseInputListener createDockingListener( ) 279 { 280 return new MetalDockingListener( toolBar ); 281 } 282 283 /** 284 * Sets the offset of the mouse cursor inside the DragWindow. 285 * 286 * @param p the offset 287 */ setDragOffset(Point p)288 protected void setDragOffset(Point p) { 289 if (!GraphicsEnvironment.isHeadless()) { 290 if (dragWindow == null) { 291 dragWindow = createDragWindow(toolBar); 292 } 293 dragWindow.setOffset(p); 294 } 295 } 296 297 /** 298 * If necessary paints the background of the component, then invokes 299 * <code>paint</code>. 300 * 301 * @param g Graphics to paint to 302 * @param c JComponent painting on 303 * @throws NullPointerException if <code>g</code> or <code>c</code> is 304 * null 305 * @see javax.swing.plaf.ComponentUI#update 306 * @see javax.swing.plaf.ComponentUI#paint 307 * @since 1.5 308 */ update(Graphics g, JComponent c)309 public void update(Graphics g, JComponent c) { 310 if (g == null) { 311 throw new NullPointerException("graphics must be non-null"); 312 } 313 if (c.isOpaque() && (c.getBackground() instanceof UIResource) && 314 ((JToolBar)c).getOrientation() == 315 JToolBar.HORIZONTAL && UIManager.get( 316 "MenuBar.gradient") != null) { 317 JRootPane rp = SwingUtilities.getRootPane(c); 318 JMenuBar mb = (JMenuBar)findRegisteredComponentOfType( 319 c, JMenuBar.class); 320 if (mb != null && mb.isOpaque() && 321 (mb.getBackground() instanceof UIResource)) { 322 Point point = new Point(0, 0); 323 point = SwingUtilities.convertPoint(c, point, rp); 324 int x = point.x; 325 int y = point.y; 326 point.x = point.y = 0; 327 point = SwingUtilities.convertPoint(mb, point, rp); 328 if (point.x == x && y == point.y + mb.getHeight() && 329 mb.getWidth() == c.getWidth() && 330 MetalUtils.drawGradient(c, g, "MenuBar.gradient", 331 0, -mb.getHeight(), c.getWidth(), c.getHeight() + 332 mb.getHeight(), true)) { 333 setLastMenuBar(mb); 334 paint(g, c); 335 return; 336 } 337 } 338 if (MetalUtils.drawGradient(c, g, "MenuBar.gradient", 339 0, 0, c.getWidth(), c.getHeight(), true)) { 340 setLastMenuBar(null); 341 paint(g, c); 342 return; 343 } 344 } 345 setLastMenuBar(null); 346 super.update(g, c); 347 } 348 setLastMenuBar(JMenuBar lastMenuBar)349 private void setLastMenuBar(JMenuBar lastMenuBar) { 350 if (MetalLookAndFeel.usingOcean()) { 351 if (this.lastMenuBar != lastMenuBar) { 352 // The menubar we previously touched has changed, force it 353 // to repaint. 354 if (this.lastMenuBar != null) { 355 this.lastMenuBar.repaint(); 356 } 357 if (lastMenuBar != null) { 358 lastMenuBar.repaint(); 359 } 360 this.lastMenuBar = lastMenuBar; 361 } 362 } 363 } 364 365 /** 366 * No longer used. The class cannot be removed for compatibility reasons. 367 */ 368 protected class MetalContainerListener 369 extends BasicToolBarUI.ToolBarContListener { 370 /** 371 * Constructs a {@code MetalContainerListener}. 372 */ MetalContainerListener()373 protected MetalContainerListener() {} 374 } 375 376 /** 377 * No longer used. The class cannot be removed for compatibility reasons. 378 */ 379 protected class MetalRolloverListener 380 extends BasicToolBarUI.PropertyListener { 381 /** 382 * Constructs a {@code MetalRolloverListener}. 383 */ MetalRolloverListener()384 protected MetalRolloverListener() {} 385 } 386 387 /** 388 * {@code DockingListener} for {@code MetalToolBarUI}. 389 */ 390 protected class MetalDockingListener extends DockingListener { 391 private boolean pressedInBumps = false; 392 393 /** 394 * Constructs the {@code MetalDockingListener}. 395 * 396 * @param t an instance of {@code JToolBar} 397 */ MetalDockingListener(JToolBar t)398 public MetalDockingListener(JToolBar t) { 399 super(t); 400 } 401 mousePressed(MouseEvent e)402 public void mousePressed(MouseEvent e) { 403 super.mousePressed(e); 404 if (!toolBar.isEnabled()) { 405 return; 406 } 407 pressedInBumps = false; 408 Rectangle bumpRect = new Rectangle(); 409 410 if (toolBar.getOrientation() == JToolBar.HORIZONTAL) { 411 int x = MetalUtils.isLeftToRight(toolBar) ? 0 : toolBar.getSize().width-14; 412 bumpRect.setBounds(x, 0, 14, toolBar.getSize().height); 413 } else { // vertical 414 bumpRect.setBounds(0, 0, toolBar.getSize().width, 14); 415 } 416 if (bumpRect.contains(e.getPoint())) { 417 pressedInBumps = true; 418 Point dragOffset = e.getPoint(); 419 if (!MetalUtils.isLeftToRight(toolBar)) { 420 dragOffset.x -= (toolBar.getSize().width 421 - toolBar.getPreferredSize().width); 422 } 423 setDragOffset(dragOffset); 424 } 425 } 426 mouseDragged(MouseEvent e)427 public void mouseDragged(MouseEvent e) { 428 if (pressedInBumps) { 429 super.mouseDragged(e); 430 } 431 } 432 } // end class MetalDockingListener 433 } 434