1 /* 2 * Copyright (c) 1998, 2013, 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 * Registers the specified component. 90 */ 91 synchronized static void register(JComponent c) { 92 if (c == null) { 93 // Exception is thrown as convenience for callers that are 94 // typed to throw an NPE. 95 throw new NullPointerException("JComponent must be non-null"); 96 } 97 components.add(new WeakReference<JComponent>(c)); 98 } 99 100 /** 101 * Unregisters the specified component. 102 */ 103 synchronized static void unregister(JComponent c) { 104 for (int counter = components.size() - 1; counter >= 0; counter--) { 105 // Search for the component, removing any flushed references 106 // along the way. 107 JComponent target = components.get(counter).get(); 108 109 if (target == c || target == null) { 110 components.remove(counter); 111 } 112 } 113 } 114 115 /** 116 * Finds a previously registered component of class <code>target</code> 117 * that shares the JRootPane ancestor of <code>from</code>. 118 */ 119 synchronized static Object findRegisteredComponentOfType(JComponent from, 120 Class<?> target) { 121 JRootPane rp = SwingUtilities.getRootPane(from); 122 if (rp != null) { 123 for (int counter = components.size() - 1; counter >= 0; counter--){ 124 Object component = ((WeakReference)components.get(counter)). 125 get(); 126 127 if (component == null) { 128 // WeakReference has gone away, remove the WeakReference 129 components.remove(counter); 130 } 131 else if (target.isInstance(component) && SwingUtilities. 132 getRootPane((Component)component) == rp) { 133 return component; 134 } 135 } 136 } 137 return null; 138 } 139 140 /** 141 * Returns true if the passed in JMenuBar is above a horizontal 142 * JToolBar. 143 */ 144 static boolean doesMenuBarBorderToolBar(JMenuBar c) { 145 JToolBar tb = (JToolBar)MetalToolBarUI. 146 findRegisteredComponentOfType(c, JToolBar.class); 147 if (tb != null && tb.getOrientation() == JToolBar.HORIZONTAL) { 148 JRootPane rp = SwingUtilities.getRootPane(c); 149 Point point = new Point(0, 0); 150 point = SwingUtilities.convertPoint(c, point, rp); 151 int menuX = point.x; 152 int menuY = point.y; 153 point.x = point.y = 0; 154 point = SwingUtilities.convertPoint(tb, point, rp); 155 return (point.x == menuX && menuY + c.getHeight() == point.y && 156 c.getWidth() == tb.getWidth()); 157 } 158 return false; 159 } 160 161 public static ComponentUI createUI( JComponent c ) 162 { 163 return new MetalToolBarUI(); 164 } 165 166 public void installUI( JComponent c ) 167 { 168 super.installUI( c ); 169 register(c); 170 } 171 172 public void uninstallUI( JComponent c ) 173 { 174 super.uninstallUI( c ); 175 nonRolloverBorder = null; 176 unregister(c); 177 } 178 179 protected void installListeners() { 180 super.installListeners(); 181 182 contListener = createContainerListener(); 183 if (contListener != null) { 184 toolBar.addContainerListener(contListener); 185 } 186 rolloverListener = createRolloverListener(); 187 if (rolloverListener != null) { 188 toolBar.addPropertyChangeListener(rolloverListener); 189 } 190 } 191 192 protected void uninstallListeners() { 193 super.uninstallListeners(); 194 195 if (contListener != null) { 196 toolBar.removeContainerListener(contListener); 197 } 198 rolloverListener = createRolloverListener(); 199 if (rolloverListener != null) { 200 toolBar.removePropertyChangeListener(rolloverListener); 201 } 202 } 203 204 protected Border createRolloverBorder() { 205 return super.createRolloverBorder(); 206 } 207 208 protected Border createNonRolloverBorder() { 209 return super.createNonRolloverBorder(); 210 } 211 212 213 /** 214 * Creates a non rollover border for Toggle buttons in the toolbar. 215 */ 216 private Border createNonRolloverToggleBorder() { 217 return createNonRolloverBorder(); 218 } 219 220 protected void setBorderToNonRollover(Component c) { 221 if (c instanceof JToggleButton && !(c instanceof JCheckBox)) { 222 // 4735514, 4886944: The method createNonRolloverToggleBorder() is 223 // private in BasicToolBarUI so we can't override it. We still need 224 // to call super from this method so that it can save away the 225 // original border and then we install ours. 226 227 // Before calling super we get a handle to the old border, because 228 // super will install a non-UIResource border that we can't 229 // distinguish from one provided by an application. 230 JToggleButton b = (JToggleButton)c; 231 Border border = b.getBorder(); 232 super.setBorderToNonRollover(c); 233 if (border instanceof UIResource) { 234 if (nonRolloverBorder == null) { 235 nonRolloverBorder = createNonRolloverToggleBorder(); 236 } 237 b.setBorder(nonRolloverBorder); 238 } 239 } else { 240 super.setBorderToNonRollover(c); 241 } 242 } 243 244 245 /** 246 * Creates a container listener that will be added to the JToolBar. 247 * If this method returns null then it will not be added to the 248 * toolbar. 249 * 250 * @return an instance of a <code>ContainerListener</code> or null 251 */ 252 protected ContainerListener createContainerListener() { 253 return null; 254 } 255 256 /** 257 * Creates a property change 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>PropertyChangeListener</code> or null 262 */ 263 protected PropertyChangeListener createRolloverListener() { 264 return null; 265 } 266 267 protected MouseInputListener createDockingListener( ) 268 { 269 return new MetalDockingListener( toolBar ); 270 } 271 272 protected void setDragOffset(Point p) { 273 if (!GraphicsEnvironment.isHeadless()) { 274 if (dragWindow == null) { 275 dragWindow = createDragWindow(toolBar); 276 } 277 dragWindow.setOffset(p); 278 } 279 } 280 281 /** 282 * If necessary paints the background of the component, then invokes 283 * <code>paint</code>. 284 * 285 * @param g Graphics to paint to 286 * @param c JComponent painting on 287 * @throws NullPointerException if <code>g</code> or <code>c</code> is 288 * null 289 * @see javax.swing.plaf.ComponentUI#update 290 * @see javax.swing.plaf.ComponentUI#paint 291 * @since 1.5 292 */ 293 public void update(Graphics g, JComponent c) { 294 if (g == null) { 295 throw new NullPointerException("graphics must be non-null"); 296 } 297 if (c.isOpaque() && (c.getBackground() instanceof UIResource) && 298 ((JToolBar)c).getOrientation() == 299 JToolBar.HORIZONTAL && UIManager.get( 300 "MenuBar.gradient") != null) { 301 JRootPane rp = SwingUtilities.getRootPane(c); 302 JMenuBar mb = (JMenuBar)findRegisteredComponentOfType( 303 c, JMenuBar.class); 304 if (mb != null && mb.isOpaque() && 305 (mb.getBackground() instanceof UIResource)) { 306 Point point = new Point(0, 0); 307 point = SwingUtilities.convertPoint(c, point, rp); 308 int x = point.x; 309 int y = point.y; 310 point.x = point.y = 0; 311 point = SwingUtilities.convertPoint(mb, point, rp); 312 if (point.x == x && y == point.y + mb.getHeight() && 313 mb.getWidth() == c.getWidth() && 314 MetalUtils.drawGradient(c, g, "MenuBar.gradient", 315 0, -mb.getHeight(), c.getWidth(), c.getHeight() + 316 mb.getHeight(), true)) { 317 setLastMenuBar(mb); 318 paint(g, c); 319 return; 320 } 321 } 322 if (MetalUtils.drawGradient(c, g, "MenuBar.gradient", 323 0, 0, c.getWidth(), c.getHeight(), true)) { 324 setLastMenuBar(null); 325 paint(g, c); 326 return; 327 } 328 } 329 setLastMenuBar(null); 330 super.update(g, c); 331 } 332 333 private void setLastMenuBar(JMenuBar lastMenuBar) { 334 if (MetalLookAndFeel.usingOcean()) { 335 if (this.lastMenuBar != lastMenuBar) { 336 // The menubar we previously touched has changed, force it 337 // to repaint. 338 if (this.lastMenuBar != null) { 339 this.lastMenuBar.repaint(); 340 } 341 if (lastMenuBar != null) { 342 lastMenuBar.repaint(); 343 } 344 this.lastMenuBar = lastMenuBar; 345 } 346 } 347 } 348 349 // No longer used. Cannot remove for compatibility reasons 350 protected class MetalContainerListener 351 extends BasicToolBarUI.ToolBarContListener {} 352 353 // No longer used. Cannot remove for compatibility reasons 354 protected class MetalRolloverListener 355 extends BasicToolBarUI.PropertyListener {} 356 357 protected class MetalDockingListener extends DockingListener { 358 private boolean pressedInBumps = false; 359 360 public MetalDockingListener(JToolBar t) { 361 super(t); 362 } 363 364 public void mousePressed(MouseEvent e) { 365 super.mousePressed(e); 366 if (!toolBar.isEnabled()) { 367 return; 368 } 369 pressedInBumps = false; 370 Rectangle bumpRect = new Rectangle(); 371 372 if (toolBar.getOrientation() == JToolBar.HORIZONTAL) { 373 int x = MetalUtils.isLeftToRight(toolBar) ? 0 : toolBar.getSize().width-14; 374 bumpRect.setBounds(x, 0, 14, toolBar.getSize().height); 375 } else { // vertical 376 bumpRect.setBounds(0, 0, toolBar.getSize().width, 14); 377 } 378 if (bumpRect.contains(e.getPoint())) { 379 pressedInBumps = true; 380 Point dragOffset = e.getPoint(); 381 if (!MetalUtils.isLeftToRight(toolBar)) { 382 dragOffset.x -= (toolBar.getSize().width 383 - toolBar.getPreferredSize().width); 384 } 385 setDragOffset(dragOffset); 386 } 387 } 388 389 public void mouseDragged(MouseEvent e) { 390 if (pressedInBumps) { 391 super.mouseDragged(e); 392 } 393 } 394 } // end class MetalDockingListener 395 }