1 /* 2 * Copyright (c) 2002, 2020, 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 java.awt.Component; 29 import java.awt.Container; 30 import java.awt.Dimension; 31 import java.awt.Graphics; 32 import java.awt.Insets; 33 import java.awt.LayoutManager; 34 import java.awt.Rectangle; 35 import java.beans.PropertyChangeEvent; 36 import java.beans.PropertyChangeListener; 37 import javax.swing.Box; 38 import javax.swing.Icon; 39 import javax.swing.JComponent; 40 import javax.swing.JSeparator; 41 import javax.swing.JToolBar; 42 import javax.swing.plaf.ComponentUI; 43 import javax.swing.plaf.basic.BasicToolBarUI; 44 45 /** 46 * Provides the Synth L&F UI delegate for 47 * {@link javax.swing.JToolBar}. 48 * 49 * @since 1.7 50 */ 51 public class SynthToolBarUI extends BasicToolBarUI 52 implements PropertyChangeListener, SynthUI { 53 private Icon handleIcon = null; 54 private Rectangle contentRect = new Rectangle(); 55 56 private SynthStyle style; 57 private SynthStyle contentStyle; 58 private SynthStyle dragWindowStyle; 59 60 /** 61 * 62 * Constructs a {@code SynthToolBarUI}. 63 */ SynthToolBarUI()64 public SynthToolBarUI() {} 65 66 /** 67 * Creates a new UI object for the given component. 68 * 69 * @param c component to create UI object for 70 * @return the UI object 71 */ createUI(JComponent c)72 public static ComponentUI createUI(JComponent c) { 73 return new SynthToolBarUI(); 74 } 75 76 /** 77 * {@inheritDoc} 78 */ 79 @Override installDefaults()80 protected void installDefaults() { 81 toolBar.setLayout(createLayout()); 82 updateStyle(toolBar); 83 } 84 85 /** 86 * {@inheritDoc} 87 */ 88 @Override installListeners()89 protected void installListeners() { 90 super.installListeners(); 91 toolBar.addPropertyChangeListener(this); 92 } 93 94 /** 95 * {@inheritDoc} 96 */ 97 @Override uninstallListeners()98 protected void uninstallListeners() { 99 super.uninstallListeners(); 100 toolBar.removePropertyChangeListener(this); 101 } 102 updateStyle(JToolBar c)103 private void updateStyle(JToolBar c) { 104 SynthContext context = getContext( 105 c, Region.TOOL_BAR_CONTENT, contentStyle, ENABLED); 106 contentStyle = SynthLookAndFeel.updateStyle(context, this); 107 108 context = getContext(c, Region.TOOL_BAR_DRAG_WINDOW, dragWindowStyle, ENABLED); 109 dragWindowStyle = SynthLookAndFeel.updateStyle(context, this); 110 111 context = getContext(c, ENABLED); 112 SynthStyle oldStyle = style; 113 114 style = SynthLookAndFeel.updateStyle(context, this); 115 if (oldStyle != style) { 116 handleIcon = 117 style.getIcon(context, "ToolBar.handleIcon"); 118 if (oldStyle != null) { 119 uninstallKeyboardActions(); 120 installKeyboardActions(); 121 } 122 } 123 } 124 125 /** 126 * {@inheritDoc} 127 */ 128 @Override uninstallDefaults()129 protected void uninstallDefaults() { 130 SynthContext context = getContext(toolBar, ENABLED); 131 132 style.uninstallDefaults(context); 133 style = null; 134 135 handleIcon = null; 136 137 context = getContext(toolBar, Region.TOOL_BAR_CONTENT, 138 contentStyle, ENABLED); 139 contentStyle.uninstallDefaults(context); 140 contentStyle = null; 141 142 context = getContext(toolBar, Region.TOOL_BAR_DRAG_WINDOW, 143 dragWindowStyle, ENABLED); 144 dragWindowStyle.uninstallDefaults(context); 145 dragWindowStyle = null; 146 147 toolBar.setLayout(null); 148 } 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override installComponents()154 protected void installComponents() {} 155 156 /** 157 * {@inheritDoc} 158 */ 159 @Override uninstallComponents()160 protected void uninstallComponents() {} 161 162 /** 163 * Creates a {@code LayoutManager} to use with the toolbar. 164 * 165 * @return a {@code LayoutManager} instance 166 */ createLayout()167 protected LayoutManager createLayout() { 168 return new SynthToolBarLayoutManager(); 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override getContext(JComponent c)175 public SynthContext getContext(JComponent c) { 176 return getContext(c, SynthLookAndFeel.getComponentState(c)); 177 } 178 getContext(JComponent c, int state)179 private SynthContext getContext(JComponent c, int state) { 180 return SynthContext.getContext(c, style, state); 181 } 182 getContext(JComponent c, Region region, SynthStyle style)183 private SynthContext getContext(JComponent c, Region region, SynthStyle style) { 184 return SynthContext.getContext(c, region, 185 style, getComponentState(c, region)); 186 } 187 getContext(JComponent c, Region region, SynthStyle style, int state)188 private SynthContext getContext(JComponent c, Region region, 189 SynthStyle style, int state) { 190 return SynthContext.getContext(c, region, style, state); 191 } 192 getComponentState(JComponent c, Region region)193 private int getComponentState(JComponent c, Region region) { 194 return SynthLookAndFeel.getComponentState(c); 195 } 196 197 /** 198 * Notifies this UI delegate to repaint the specified component. 199 * This method paints the component background, then calls 200 * the {@link #paint(SynthContext,Graphics)} method. 201 * 202 * <p>In general, this method does not need to be overridden by subclasses. 203 * All Look and Feel rendering code should reside in the {@code paint} method. 204 * 205 * @param g the {@code Graphics} object used for painting 206 * @param c the component being painted 207 * @see #paint(SynthContext,Graphics) 208 */ 209 @Override update(Graphics g, JComponent c)210 public void update(Graphics g, JComponent c) { 211 SynthContext context = getContext(c); 212 213 SynthLookAndFeel.update(context, g); 214 context.getPainter().paintToolBarBackground(context, 215 g, 0, 0, c.getWidth(), c.getHeight(), 216 toolBar.getOrientation()); 217 paint(context, g); 218 } 219 220 /** 221 * Paints the specified component according to the Look and Feel. 222 * <p>This method is not used by Synth Look and Feel. 223 * Painting is handled by the {@link #paint(SynthContext,Graphics)} method. 224 * 225 * @param g the {@code Graphics} object used for painting 226 * @param c the component being painted 227 * @see #paint(SynthContext,Graphics) 228 */ 229 @Override paint(Graphics g, JComponent c)230 public void paint(Graphics g, JComponent c) { 231 SynthContext context = getContext(c); 232 233 paint(context, g); 234 } 235 236 /** 237 * {@inheritDoc} 238 */ 239 @Override paintBorder(SynthContext context, Graphics g, int x, int y, int w, int h)240 public void paintBorder(SynthContext context, Graphics g, int x, 241 int y, int w, int h) { 242 context.getPainter().paintToolBarBorder(context, g, x, y, w, h, 243 toolBar.getOrientation()); 244 } 245 246 /** 247 * This implementation does nothing, because the {@code rollover} 248 * property of the {@code JToolBar} class is not used 249 * in the Synth Look and Feel. 250 */ 251 @Override setBorderToNonRollover(Component c)252 protected void setBorderToNonRollover(Component c) {} 253 254 /** 255 * This implementation does nothing, because the {@code rollover} 256 * property of the {@code JToolBar} class is not used 257 * in the Synth Look and Feel. 258 */ 259 @Override setBorderToRollover(Component c)260 protected void setBorderToRollover(Component c) {} 261 262 /** 263 * This implementation does nothing, because the {@code rollover} 264 * property of the {@code JToolBar} class is not used 265 * in the Synth Look and Feel. 266 */ 267 @Override setBorderToNormal(Component c)268 protected void setBorderToNormal(Component c) {} 269 270 /** 271 * Paints the toolbar. 272 * 273 * @param context context for the component being painted 274 * @param g the {@code Graphics} object used for painting 275 * @see #update(Graphics,JComponent) 276 */ paint(SynthContext context, Graphics g)277 protected void paint(SynthContext context, Graphics g) { 278 if (handleIcon != null && toolBar.isFloatable()) { 279 int startX = toolBar.getComponentOrientation().isLeftToRight() ? 280 0 : toolBar.getWidth() - 281 SynthGraphicsUtils.getIconWidth(handleIcon, context); 282 SynthGraphicsUtils.paintIcon(handleIcon, context, g, startX, 0, 283 SynthGraphicsUtils.getIconWidth(handleIcon, context), 284 SynthGraphicsUtils.getIconHeight(handleIcon, context)); 285 } 286 287 SynthContext subcontext = getContext( 288 toolBar, Region.TOOL_BAR_CONTENT, contentStyle); 289 paintContent(subcontext, g, contentRect); 290 } 291 292 /** 293 * Paints the toolbar content. 294 * 295 * @param context context for the component being painted 296 * @param g {@code Graphics} object used for painting 297 * @param bounds bounding box for the toolbar 298 */ paintContent(SynthContext context, Graphics g, Rectangle bounds)299 protected void paintContent(SynthContext context, Graphics g, 300 Rectangle bounds) { 301 SynthLookAndFeel.updateSubregion(context, g, bounds); 302 context.getPainter().paintToolBarContentBackground(context, g, 303 bounds.x, bounds.y, bounds.width, bounds.height, 304 toolBar.getOrientation()); 305 context.getPainter().paintToolBarContentBorder(context, g, 306 bounds.x, bounds.y, bounds.width, bounds.height, 307 toolBar.getOrientation()); 308 } 309 310 /** 311 * {@inheritDoc} 312 */ 313 @Override paintDragWindow(Graphics g)314 protected void paintDragWindow(Graphics g) { 315 int w = dragWindow.getWidth(); 316 int h = dragWindow.getHeight(); 317 SynthContext context = getContext( 318 toolBar, Region.TOOL_BAR_DRAG_WINDOW, dragWindowStyle); 319 SynthLookAndFeel.updateSubregion( 320 context, g, new Rectangle(0, 0, w, h)); 321 context.getPainter().paintToolBarDragWindowBackground(context, 322 g, 0, 0, w, h, 323 dragWindow.getOrientation()); 324 context.getPainter().paintToolBarDragWindowBorder(context, g, 0, 0, w, h, 325 dragWindow.getOrientation()); 326 } 327 328 // 329 // PropertyChangeListener 330 // 331 332 /** 333 * {@inheritDoc} 334 */ 335 @Override propertyChange(PropertyChangeEvent e)336 public void propertyChange(PropertyChangeEvent e) { 337 if (SynthLookAndFeel.shouldUpdateStyle(e)) { 338 updateStyle((JToolBar)e.getSource()); 339 } 340 } 341 342 343 class SynthToolBarLayoutManager implements LayoutManager { addLayoutComponent(String name, Component comp)344 public void addLayoutComponent(String name, Component comp) {} 345 removeLayoutComponent(Component comp)346 public void removeLayoutComponent(Component comp) {} 347 minimumLayoutSize(Container parent)348 public Dimension minimumLayoutSize(Container parent) { 349 JToolBar tb = (JToolBar)parent; 350 Insets insets = tb.getInsets(); 351 Dimension dim = new Dimension(); 352 SynthContext context = getContext(tb); 353 354 if (tb.getOrientation() == JToolBar.HORIZONTAL) { 355 dim.width = tb.isFloatable() ? 356 SynthGraphicsUtils.getIconWidth(handleIcon, context) : 0; 357 Dimension compDim; 358 for (int i = 0; i < tb.getComponentCount(); i++) { 359 Component component = tb.getComponent(i); 360 if (component.isVisible()) { 361 compDim = component.getMinimumSize(); 362 dim.width += compDim.width; 363 dim.height = Math.max(dim.height, compDim.height); 364 } 365 } 366 } else { 367 dim.height = tb.isFloatable() ? 368 SynthGraphicsUtils.getIconHeight(handleIcon, context) : 0; 369 Dimension compDim; 370 for (int i = 0; i < tb.getComponentCount(); i++) { 371 Component component = tb.getComponent(i); 372 if (component.isVisible()) { 373 compDim = component.getMinimumSize(); 374 dim.width = Math.max(dim.width, compDim.width); 375 dim.height += compDim.height; 376 } 377 } 378 } 379 dim.width += insets.left + insets.right; 380 dim.height += insets.top + insets.bottom; 381 382 return dim; 383 } 384 preferredLayoutSize(Container parent)385 public Dimension preferredLayoutSize(Container parent) { 386 JToolBar tb = (JToolBar)parent; 387 Insets insets = tb.getInsets(); 388 Dimension dim = new Dimension(); 389 SynthContext context = getContext(tb); 390 391 if (tb.getOrientation() == JToolBar.HORIZONTAL) { 392 dim.width = tb.isFloatable() ? 393 SynthGraphicsUtils.getIconWidth(handleIcon, context) : 0; 394 Dimension compDim; 395 for (int i = 0; i < tb.getComponentCount(); i++) { 396 Component component = tb.getComponent(i); 397 if (component.isVisible()) { 398 compDim = component.getPreferredSize(); 399 dim.width += compDim.width; 400 dim.height = Math.max(dim.height, compDim.height); 401 } 402 } 403 } else { 404 dim.height = tb.isFloatable() ? 405 SynthGraphicsUtils.getIconHeight(handleIcon, context) : 0; 406 Dimension compDim; 407 for (int i = 0; i < tb.getComponentCount(); i++) { 408 Component component = tb.getComponent(i); 409 if (component.isVisible()) { 410 compDim = component.getPreferredSize(); 411 dim.width = Math.max(dim.width, compDim.width); 412 dim.height += compDim.height; 413 } 414 } 415 } 416 dim.width += insets.left + insets.right; 417 dim.height += insets.top + insets.bottom; 418 419 return dim; 420 } 421 layoutContainer(Container parent)422 public void layoutContainer(Container parent) { 423 JToolBar tb = (JToolBar)parent; 424 Insets insets = tb.getInsets(); 425 boolean ltr = tb.getComponentOrientation().isLeftToRight(); 426 SynthContext context = getContext(tb); 427 428 Component c; 429 Dimension d; 430 431 // JToolBar by default uses a somewhat modified BoxLayout as 432 // its layout manager. For compatibility reasons, we want to 433 // support Box "glue" as a way to move things around on the 434 // toolbar. "glue" is represented in BoxLayout as a Box.Filler 435 // with a minimum and preferred size of (0,0). 436 // So what we do here is find the number of such glue fillers 437 // and figure out how much space should be allocated to them. 438 int glueCount = 0; 439 for (int i=0; i<tb.getComponentCount(); i++) { 440 if (isGlue(tb.getComponent(i))) glueCount++; 441 } 442 443 if (tb.getOrientation() == JToolBar.HORIZONTAL) { 444 int handleWidth = tb.isFloatable() ? 445 SynthGraphicsUtils.getIconWidth(handleIcon, context) : 0; 446 447 // Note: contentRect does not take insets into account 448 // since it is used for determining the bounds that are 449 // passed to paintToolBarContentBackground(). 450 contentRect.x = ltr ? handleWidth : 0; 451 contentRect.y = 0; 452 contentRect.width = tb.getWidth() - handleWidth; 453 contentRect.height = tb.getHeight(); 454 455 // However, we do take the insets into account here for 456 // the purposes of laying out the toolbar child components. 457 int x = ltr ? 458 handleWidth + insets.left : 459 tb.getWidth() - handleWidth - insets.right; 460 int baseY = insets.top; 461 int baseH = tb.getHeight() - insets.top - insets.bottom; 462 463 // we need to get the minimum width for laying things out 464 // so that we can calculate how much empty space needs to 465 // be distributed among the "glue", if any 466 int extraSpacePerGlue = 0; 467 if (glueCount > 0) { 468 int minWidth = preferredLayoutSize(parent).width; 469 extraSpacePerGlue = (tb.getWidth() - minWidth) / glueCount; 470 if (extraSpacePerGlue < 0) extraSpacePerGlue = 0; 471 } 472 473 for (int i = 0; i < tb.getComponentCount(); i++) { 474 c = tb.getComponent(i); 475 if (c.isVisible()) { 476 d = c.getPreferredSize(); 477 int y, h; 478 if (d.height >= baseH || c instanceof JSeparator) { 479 // Fill available height 480 y = baseY; 481 h = baseH; 482 } else { 483 // Center component vertically in the available space 484 y = baseY + (baseH / 2) - (d.height / 2); 485 h = d.height; 486 } 487 //if the component is a "glue" component then add to its 488 //width the extraSpacePerGlue it is due 489 if (isGlue(c)) d.width += extraSpacePerGlue; 490 c.setBounds(ltr ? x : x - d.width, y, d.width, h); 491 x = ltr ? x + d.width : x - d.width; 492 } 493 } 494 } else { 495 int handleHeight = tb.isFloatable() ? 496 SynthGraphicsUtils.getIconHeight(handleIcon, context) : 0; 497 498 // See notes above regarding the use of insets 499 contentRect.x = 0; 500 contentRect.y = handleHeight; 501 contentRect.width = tb.getWidth(); 502 contentRect.height = tb.getHeight() - handleHeight; 503 504 int baseX = insets.left; 505 int baseW = tb.getWidth() - insets.left - insets.right; 506 int y = handleHeight + insets.top; 507 508 // we need to get the minimum height for laying things out 509 // so that we can calculate how much empty space needs to 510 // be distributed among the "glue", if any 511 int extraSpacePerGlue = 0; 512 if (glueCount > 0) { 513 int minHeight = minimumLayoutSize(parent).height; 514 extraSpacePerGlue = (tb.getHeight() - minHeight) / glueCount; 515 if (extraSpacePerGlue < 0) extraSpacePerGlue = 0; 516 } 517 518 for (int i = 0; i < tb.getComponentCount(); i++) { 519 c = tb.getComponent(i); 520 if (c.isVisible()) { 521 d = c.getPreferredSize(); 522 int x, w; 523 if (d.width >= baseW || c instanceof JSeparator) { 524 // Fill available width 525 x = baseX; 526 w = baseW; 527 } else { 528 // Center component horizontally in the available space 529 x = baseX + (baseW / 2) - (d.width / 2); 530 w = d.width; 531 } 532 //if the component is a "glue" component then add to its 533 //height the extraSpacePerGlue it is due 534 if (isGlue(c)) d.height += extraSpacePerGlue; 535 c.setBounds(x, y, w, d.height); 536 y += d.height; 537 } 538 } 539 } 540 } 541 isGlue(Component c)542 private boolean isGlue(Component c) { 543 if (c.isVisible() && c instanceof Box.Filler) { 544 Box.Filler f = (Box.Filler)c; 545 Dimension min = f.getMinimumSize(); 546 Dimension pref = f.getPreferredSize(); 547 return min.width == 0 && min.height == 0 && 548 pref.width == 0 && pref.height == 0; 549 } 550 return false; 551 } 552 } 553 } 554