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 26package javax.swing.plaf.metal; 27 28import javax.swing.*; 29import java.awt.Color; 30import java.awt.Component; 31import java.awt.Container; 32import java.awt.Dimension; 33import java.awt.Frame; 34import java.awt.Graphics; 35import java.awt.GraphicsEnvironment; 36import java.awt.Insets; 37import java.awt.Point; 38import java.awt.Rectangle; 39import java.awt.event.*; 40import java.lang.ref.WeakReference; 41import java.util.*; 42 43import java.beans.PropertyChangeListener; 44 45import javax.swing.event.*; 46import javax.swing.border.*; 47import javax.swing.plaf.*; 48import 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 */ 56public 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 static synchronized 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 static synchronized 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 static synchronized 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 /** 162 * Constructs an instance of {@code MetalToolBarUI}. 163 * 164 * @param c a component 165 * @return an instance of {@code MetalToolBarUI} 166 */ 167 public static ComponentUI createUI( JComponent c ) 168 { 169 return new MetalToolBarUI(); 170 } 171 172 public void installUI( JComponent c ) 173 { 174 super.installUI( c ); 175 register(c); 176 } 177 178 public void uninstallUI( JComponent c ) 179 { 180 super.uninstallUI( c ); 181 nonRolloverBorder = null; 182 unregister(c); 183 } 184 185 protected void installListeners() { 186 super.installListeners(); 187 188 contListener = createContainerListener(); 189 if (contListener != null) { 190 toolBar.addContainerListener(contListener); 191 } 192 rolloverListener = createRolloverListener(); 193 if (rolloverListener != null) { 194 toolBar.addPropertyChangeListener(rolloverListener); 195 } 196 } 197 198 protected void uninstallListeners() { 199 super.uninstallListeners(); 200 201 if (contListener != null) { 202 toolBar.removeContainerListener(contListener); 203 } 204 rolloverListener = createRolloverListener(); 205 if (rolloverListener != null) { 206 toolBar.removePropertyChangeListener(rolloverListener); 207 } 208 } 209 210 protected Border createRolloverBorder() { 211 return super.createRolloverBorder(); 212 } 213 214 protected Border createNonRolloverBorder() { 215 return super.createNonRolloverBorder(); 216 } 217 218 219 /** 220 * Creates a non rollover border for Toggle buttons in the toolbar. 221 */ 222 private Border createNonRolloverToggleBorder() { 223 return createNonRolloverBorder(); 224 } 225 226 protected void setBorderToNonRollover(Component c) { 227 if (c instanceof JToggleButton && !(c instanceof JCheckBox)) { 228 // 4735514, 4886944: The method createNonRolloverToggleBorder() is 229 // private in BasicToolBarUI so we can't override it. We still need 230 // to call super from this method so that it can save away the 231 // original border and then we install ours. 232 233 // Before calling super we get a handle to the old border, because 234 // super will install a non-UIResource border that we can't 235 // distinguish from one provided by an application. 236 JToggleButton b = (JToggleButton)c; 237 Border border = b.getBorder(); 238 super.setBorderToNonRollover(c); 239 if (border instanceof UIResource) { 240 if (nonRolloverBorder == null) { 241 nonRolloverBorder = createNonRolloverToggleBorder(); 242 } 243 b.setBorder(nonRolloverBorder); 244 } 245 } else { 246 super.setBorderToNonRollover(c); 247 } 248 } 249 250 251 /** 252 * Creates a container listener that will be added to the JToolBar. 253 * If this method returns null then it will not be added to the 254 * toolbar. 255 * 256 * @return an instance of a <code>ContainerListener</code> or null 257 */ 258 protected ContainerListener createContainerListener() { 259 return null; 260 } 261 262 /** 263 * Creates a property change listener that will be added to the JToolBar. 264 * If this method returns null then it will not be added to the 265 * toolbar. 266 * 267 * @return an instance of a <code>PropertyChangeListener</code> or null 268 */ 269 protected PropertyChangeListener createRolloverListener() { 270 return null; 271 } 272 273 protected MouseInputListener createDockingListener( ) 274 { 275 return new MetalDockingListener( toolBar ); 276 } 277 278 /** 279 * Sets the offset of the mouse cursor inside the DragWindow. 280 * 281 * @param p the offset 282 */ 283 protected void setDragOffset(Point p) { 284 if (!GraphicsEnvironment.isHeadless()) { 285 if (dragWindow == null) { 286 dragWindow = createDragWindow(toolBar); 287 } 288 dragWindow.setOffset(p); 289 } 290 } 291 292 /** 293 * If necessary paints the background of the component, then invokes 294 * <code>paint</code>. 295 * 296 * @param g Graphics to paint to 297 * @param c JComponent painting on 298 * @throws NullPointerException if <code>g</code> or <code>c</code> is 299 * null 300 * @see javax.swing.plaf.ComponentUI#update 301 * @see javax.swing.plaf.ComponentUI#paint 302 * @since 1.5 303 */ 304 public void update(Graphics g, JComponent c) { 305 if (g == null) { 306 throw new NullPointerException("graphics must be non-null"); 307 } 308 if (c.isOpaque() && (c.getBackground() instanceof UIResource) && 309 ((JToolBar)c).getOrientation() == 310 JToolBar.HORIZONTAL && UIManager.get( 311 "MenuBar.gradient") != null) { 312 JRootPane rp = SwingUtilities.getRootPane(c); 313 JMenuBar mb = (JMenuBar)findRegisteredComponentOfType( 314 c, JMenuBar.class); 315 if (mb != null && mb.isOpaque() && 316 (mb.getBackground() instanceof UIResource)) { 317 Point point = new Point(0, 0); 318 point = SwingUtilities.convertPoint(c, point, rp); 319 int x = point.x; 320 int y = point.y; 321 point.x = point.y = 0; 322 point = SwingUtilities.convertPoint(mb, point, rp); 323 if (point.x == x && y == point.y + mb.getHeight() && 324 mb.getWidth() == c.getWidth() && 325 MetalUtils.drawGradient(c, g, "MenuBar.gradient", 326 0, -mb.getHeight(), c.getWidth(), c.getHeight() + 327 mb.getHeight(), true)) { 328 setLastMenuBar(mb); 329 paint(g, c); 330 return; 331 } 332 } 333 if (MetalUtils.drawGradient(c, g, "MenuBar.gradient", 334 0, 0, c.getWidth(), c.getHeight(), true)) { 335 setLastMenuBar(null); 336 paint(g, c); 337 return; 338 } 339 } 340 setLastMenuBar(null); 341 super.update(g, c); 342 } 343 344 private void setLastMenuBar(JMenuBar lastMenuBar) { 345 if (MetalLookAndFeel.usingOcean()) { 346 if (this.lastMenuBar != lastMenuBar) { 347 // The menubar we previously touched has changed, force it 348 // to repaint. 349 if (this.lastMenuBar != null) { 350 this.lastMenuBar.repaint(); 351 } 352 if (lastMenuBar != null) { 353 lastMenuBar.repaint(); 354 } 355 this.lastMenuBar = lastMenuBar; 356 } 357 } 358 } 359 360 /** 361 * No longer used. The class cannot be removed for compatibility reasons. 362 */ 363 protected class MetalContainerListener 364 extends BasicToolBarUI.ToolBarContListener {} 365 366 /** 367 * No longer used. The class cannot be removed for compatibility reasons. 368 */ 369 protected class MetalRolloverListener 370 extends BasicToolBarUI.PropertyListener {} 371 372 /** 373 * {@code DockingListener} for {@code MetalToolBarUI}. 374 */ 375 protected class MetalDockingListener extends DockingListener { 376 private boolean pressedInBumps = false; 377 378 /** 379 * Constructs the {@code MetalDockingListener}. 380 * 381 * @param t an instance of {@code JToolBar} 382 */ 383 public MetalDockingListener(JToolBar t) { 384 super(t); 385 } 386 387 public void mousePressed(MouseEvent e) { 388 super.mousePressed(e); 389 if (!toolBar.isEnabled()) { 390 return; 391 } 392 pressedInBumps = false; 393 Rectangle bumpRect = new Rectangle(); 394 395 if (toolBar.getOrientation() == JToolBar.HORIZONTAL) { 396 int x = MetalUtils.isLeftToRight(toolBar) ? 0 : toolBar.getSize().width-14; 397 bumpRect.setBounds(x, 0, 14, toolBar.getSize().height); 398 } else { // vertical 399 bumpRect.setBounds(0, 0, toolBar.getSize().width, 14); 400 } 401 if (bumpRect.contains(e.getPoint())) { 402 pressedInBumps = true; 403 Point dragOffset = e.getPoint(); 404 if (!MetalUtils.isLeftToRight(toolBar)) { 405 dragOffset.x -= (toolBar.getSize().width 406 - toolBar.getPreferredSize().width); 407 } 408 setDragOffset(dragOffset); 409 } 410 } 411 412 public void mouseDragged(MouseEvent e) { 413 if (pressedInBumps) { 414 super.mouseDragged(e); 415 } 416 } 417 } // end class MetalDockingListener 418} 419