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