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 sun.swing.SwingUtilities2; 29 import java.awt.*; 30 import java.awt.event.*; 31 import javax.swing.*; 32 import javax.swing.border.*; 33 import javax.swing.event.InternalFrameEvent; 34 import java.util.EventListener; 35 import java.beans.PropertyChangeListener; 36 import java.beans.PropertyChangeEvent; 37 import javax.swing.plaf.basic.BasicInternalFrameTitlePane; 38 39 40 /** 41 * Class that manages a JLF title bar 42 * @author Steve Wilson 43 * @author Brian Beck 44 * @since 1.3 45 */ 46 @SuppressWarnings("serial") // Superclass is not serializable across versions 47 public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane { 48 49 /** 50 * The value {@code isPalette} 51 */ 52 protected boolean isPalette = false; 53 54 /** 55 * The palette close icon. 56 */ 57 protected Icon paletteCloseIcon; 58 59 /** 60 * The height of the palette title. 61 */ 62 protected int paletteTitleHeight; 63 64 private static final Border handyEmptyBorder = new EmptyBorder(0,0,0,0); 65 66 /** 67 * Key used to lookup Color from UIManager. If this is null, 68 * <code>getWindowTitleBackground</code> is used. 69 */ 70 private String selectedBackgroundKey; 71 /** 72 * Key used to lookup Color from UIManager. If this is null, 73 * <code>getWindowTitleForeground</code> is used. 74 */ 75 private String selectedForegroundKey; 76 /** 77 * Key used to lookup shadow color from UIManager. If this is null, 78 * <code>getPrimaryControlDarkShadow</code> is used. 79 */ 80 private String selectedShadowKey; 81 /** 82 * Boolean indicating the state of the <code>JInternalFrame</code>s 83 * closable property at <code>updateUI</code> time. 84 */ 85 private boolean wasClosable; 86 87 int buttonsWidth = 0; 88 89 MetalBumps activeBumps 90 = new MetalBumps( 0, 0, 91 MetalLookAndFeel.getPrimaryControlHighlight(), 92 MetalLookAndFeel.getPrimaryControlDarkShadow(), 93 (UIManager.get("InternalFrame.activeTitleGradient") != null) ? null : 94 MetalLookAndFeel.getPrimaryControl() ); 95 MetalBumps inactiveBumps 96 = new MetalBumps( 0, 0, 97 MetalLookAndFeel.getControlHighlight(), 98 MetalLookAndFeel.getControlDarkShadow(), 99 (UIManager.get("InternalFrame.inactiveTitleGradient") != null) ? null : 100 MetalLookAndFeel.getControl() ); 101 MetalBumps paletteBumps; 102 103 private Color activeBumpsHighlight = MetalLookAndFeel. 104 getPrimaryControlHighlight(); 105 private Color activeBumpsShadow = MetalLookAndFeel. 106 getPrimaryControlDarkShadow(); 107 108 /** 109 * Constructs a new instance of {@code MetalInternalFrameTitlePane} 110 * 111 * @param f an instance of {@code JInternalFrame} 112 */ 113 public MetalInternalFrameTitlePane(JInternalFrame f) { 114 super( f ); 115 } 116 117 public void addNotify() { 118 super.addNotify(); 119 // This is done here instead of in installDefaults as I was worried 120 // that the BasicInternalFrameUI might not be fully initialized, and 121 // that if this resets the closable state the BasicInternalFrameUI 122 // Listeners that get notified might be in an odd/uninitialized state. 123 updateOptionPaneState(); 124 } 125 126 protected void installDefaults() { 127 super.installDefaults(); 128 setFont( UIManager.getFont("InternalFrame.titleFont") ); 129 paletteTitleHeight 130 = UIManager.getInt("InternalFrame.paletteTitleHeight"); 131 paletteCloseIcon = UIManager.getIcon("InternalFrame.paletteCloseIcon"); 132 wasClosable = frame.isClosable(); 133 selectedForegroundKey = selectedBackgroundKey = null; 134 if (MetalLookAndFeel.usingOcean()) { 135 setOpaque(true); 136 } 137 } 138 139 protected void uninstallDefaults() { 140 super.uninstallDefaults(); 141 if (wasClosable != frame.isClosable()) { 142 frame.setClosable(wasClosable); 143 } 144 } 145 146 protected void createButtons() { 147 super.createButtons(); 148 149 Boolean paintActive = frame.isSelected() ? Boolean.TRUE:Boolean.FALSE; 150 iconButton.putClientProperty("paintActive", paintActive); 151 iconButton.setBorder(handyEmptyBorder); 152 153 maxButton.putClientProperty("paintActive", paintActive); 154 maxButton.setBorder(handyEmptyBorder); 155 156 closeButton.putClientProperty("paintActive", paintActive); 157 closeButton.setBorder(handyEmptyBorder); 158 159 // The palette close icon isn't opaque while the regular close icon is. 160 // This makes sure palette close buttons have the right background. 161 closeButton.setBackground(MetalLookAndFeel.getPrimaryControlShadow()); 162 163 if (MetalLookAndFeel.usingOcean()) { 164 iconButton.setContentAreaFilled(false); 165 maxButton.setContentAreaFilled(false); 166 closeButton.setContentAreaFilled(false); 167 } 168 } 169 170 /** 171 * Override the parent's method to do nothing. Metal frames do not 172 * have system menus. 173 */ 174 protected void assembleSystemMenu() {} 175 176 /** 177 * Override the parent's method to do nothing. Metal frames do not 178 * have system menus. 179 */ 180 protected void addSystemMenuItems(JMenu systemMenu) {} 181 182 /** 183 * Override the parent's method to do nothing. Metal frames do not 184 * have system menus. 185 */ 186 protected void showSystemMenu() {} 187 188 /** 189 * Override the parent's method avoid creating a menu bar. Metal frames 190 * do not have system menus. 191 */ 192 protected void addSubComponents() { 193 add(iconButton); 194 add(maxButton); 195 add(closeButton); 196 } 197 198 protected PropertyChangeListener createPropertyChangeListener() { 199 return new MetalPropertyChangeHandler(); 200 } 201 202 protected LayoutManager createLayout() { 203 return new MetalTitlePaneLayout(); 204 } 205 206 class MetalPropertyChangeHandler 207 extends BasicInternalFrameTitlePane.PropertyChangeHandler 208 { 209 public void propertyChange(PropertyChangeEvent evt) { 210 String prop = evt.getPropertyName(); 211 if( prop.equals(JInternalFrame.IS_SELECTED_PROPERTY) ) { 212 Boolean b = (Boolean)evt.getNewValue(); 213 iconButton.putClientProperty("paintActive", b); 214 closeButton.putClientProperty("paintActive", b); 215 maxButton.putClientProperty("paintActive", b); 216 } 217 else if ("JInternalFrame.messageType".equals(prop)) { 218 updateOptionPaneState(); 219 frame.repaint(); 220 } 221 super.propertyChange(evt); 222 } 223 } 224 225 class MetalTitlePaneLayout extends TitlePaneLayout { 226 public void addLayoutComponent(String name, Component c) {} 227 public void removeLayoutComponent(Component c) {} 228 public Dimension preferredLayoutSize(Container c) { 229 return minimumLayoutSize(c); 230 } 231 232 public Dimension minimumLayoutSize(Container c) { 233 // Compute width. 234 int width = 30; 235 if (frame.isClosable()) { 236 width += 21; 237 } 238 if (frame.isMaximizable()) { 239 width += 16 + (frame.isClosable() ? 10 : 4); 240 } 241 if (frame.isIconifiable()) { 242 width += 16 + (frame.isMaximizable() ? 2 : 243 (frame.isClosable() ? 10 : 4)); 244 } 245 FontMetrics fm = frame.getFontMetrics(getFont()); 246 String frameTitle = frame.getTitle(); 247 int title_w = frameTitle != null ? SwingUtilities2. 248 getTextUIDrawing(frame).getStringWidth( 249 frame, fm, frameTitle) : 0; 250 int title_length = frameTitle != null ? frameTitle.length() : 0; 251 252 if (title_length > 2) { 253 int subtitle_w = SwingUtilities2.getTextUIDrawing(frame) 254 .getStringWidth(frame, fm, 255 frame.getTitle().substring(0, 2) + "..."); 256 width += (title_w < subtitle_w) ? title_w : subtitle_w; 257 } 258 else { 259 width += title_w; 260 } 261 262 // Compute height. 263 int height; 264 if (isPalette) { 265 height = paletteTitleHeight; 266 } else { 267 int fontHeight = fm.getHeight(); 268 fontHeight += 7; 269 Icon icon = frame.getFrameIcon(); 270 int iconHeight = 0; 271 if (icon != null) { 272 // SystemMenuBar forces the icon to be 16x16 or less. 273 iconHeight = Math.min(icon.getIconHeight(), 16); 274 } 275 iconHeight += 5; 276 height = Math.max(fontHeight, iconHeight); 277 } 278 279 return new Dimension(width, height); 280 } 281 282 public void layoutContainer(Container c) { 283 boolean leftToRight = MetalUtils.isLeftToRight(frame); 284 285 int w = getWidth(); 286 int x = leftToRight ? w : 0; 287 int y = 2; 288 int spacing; 289 290 // assumes all buttons have the same dimensions 291 // these dimensions include the borders 292 int buttonHeight = closeButton.getIcon().getIconHeight(); 293 int buttonWidth = closeButton.getIcon().getIconWidth(); 294 295 if(frame.isClosable()) { 296 if (isPalette) { 297 spacing = 3; 298 x += leftToRight ? -spacing -(buttonWidth+2) : spacing; 299 closeButton.setBounds(x, y, buttonWidth+2, getHeight()-4); 300 if( !leftToRight ) x += (buttonWidth+2); 301 } else { 302 spacing = 4; 303 x += leftToRight ? -spacing -buttonWidth : spacing; 304 closeButton.setBounds(x, y, buttonWidth, buttonHeight); 305 if( !leftToRight ) x += buttonWidth; 306 } 307 } 308 309 if(frame.isMaximizable() && !isPalette ) { 310 spacing = frame.isClosable() ? 10 : 4; 311 x += leftToRight ? -spacing -buttonWidth : spacing; 312 maxButton.setBounds(x, y, buttonWidth, buttonHeight); 313 if( !leftToRight ) x += buttonWidth; 314 } 315 316 if(frame.isIconifiable() && !isPalette ) { 317 spacing = frame.isMaximizable() ? 2 318 : (frame.isClosable() ? 10 : 4); 319 x += leftToRight ? -spacing -buttonWidth : spacing; 320 iconButton.setBounds(x, y, buttonWidth, buttonHeight); 321 if( !leftToRight ) x += buttonWidth; 322 } 323 324 buttonsWidth = leftToRight ? w - x : x; 325 } 326 } 327 328 /** 329 * Paints palette. 330 * 331 * @param g a instance of {@code Graphics} 332 */ 333 public void paintPalette(Graphics g) { 334 boolean leftToRight = MetalUtils.isLeftToRight(frame); 335 336 int width = getWidth(); 337 int height = getHeight(); 338 339 if (paletteBumps == null) { 340 paletteBumps 341 = new MetalBumps(0, 0, 342 MetalLookAndFeel.getPrimaryControlHighlight(), 343 MetalLookAndFeel.getPrimaryControlInfo(), 344 MetalLookAndFeel.getPrimaryControlShadow() ); 345 } 346 347 Color background = MetalLookAndFeel.getPrimaryControlShadow(); 348 Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow(); 349 350 g.setColor(background); 351 g.fillRect(0, 0, width, height); 352 353 g.setColor( darkShadow ); 354 g.drawLine ( 0, height - 1, width, height -1); 355 356 int xOffset = leftToRight ? 4 : buttonsWidth + 4; 357 int bumpLength = width - buttonsWidth -2*4; 358 int bumpHeight = getHeight() - 4; 359 paletteBumps.setBumpArea( bumpLength, bumpHeight ); 360 paletteBumps.paintIcon( this, g, xOffset, 2); 361 } 362 363 public void paintComponent(Graphics g) { 364 if(isPalette) { 365 paintPalette(g); 366 return; 367 } 368 369 boolean leftToRight = MetalUtils.isLeftToRight(frame); 370 boolean isSelected = frame.isSelected(); 371 372 int width = getWidth(); 373 int height = getHeight(); 374 375 Color background = null; 376 Color foreground = null; 377 Color shadow = null; 378 379 MetalBumps bumps; 380 String gradientKey; 381 382 if (isSelected) { 383 if (!MetalLookAndFeel.usingOcean()) { 384 closeButton.setContentAreaFilled(true); 385 maxButton.setContentAreaFilled(true); 386 iconButton.setContentAreaFilled(true); 387 } 388 if (selectedBackgroundKey != null) { 389 background = UIManager.getColor(selectedBackgroundKey); 390 } 391 if (background == null) { 392 background = MetalLookAndFeel.getWindowTitleBackground(); 393 } 394 if (selectedForegroundKey != null) { 395 foreground = UIManager.getColor(selectedForegroundKey); 396 } 397 if (selectedShadowKey != null) { 398 shadow = UIManager.getColor(selectedShadowKey); 399 } 400 if (shadow == null) { 401 shadow = MetalLookAndFeel.getPrimaryControlDarkShadow(); 402 } 403 if (foreground == null) { 404 foreground = MetalLookAndFeel.getWindowTitleForeground(); 405 } 406 activeBumps.setBumpColors(activeBumpsHighlight, activeBumpsShadow, 407 UIManager.get("InternalFrame.activeTitleGradient") != 408 null ? null : background); 409 bumps = activeBumps; 410 gradientKey = "InternalFrame.activeTitleGradient"; 411 } else { 412 if (!MetalLookAndFeel.usingOcean()) { 413 closeButton.setContentAreaFilled(false); 414 maxButton.setContentAreaFilled(false); 415 iconButton.setContentAreaFilled(false); 416 } 417 background = MetalLookAndFeel.getWindowTitleInactiveBackground(); 418 foreground = MetalLookAndFeel.getWindowTitleInactiveForeground(); 419 shadow = MetalLookAndFeel.getControlDarkShadow(); 420 bumps = inactiveBumps; 421 gradientKey = "InternalFrame.inactiveTitleGradient"; 422 } 423 424 if (!MetalUtils.drawGradient(this, g, gradientKey, 0, 0, width, 425 height, true)) { 426 g.setColor(background); 427 g.fillRect(0, 0, width, height); 428 } 429 430 g.setColor( shadow ); 431 g.drawLine ( 0, height - 1, width, height -1); 432 g.drawLine ( 0, 0, 0 ,0); 433 g.drawLine ( width - 1, 0 , width -1, 0); 434 435 436 int titleLength; 437 int xOffset = leftToRight ? 5 : width - 5; 438 String frameTitle = frame.getTitle(); 439 440 Icon icon = frame.getFrameIcon(); 441 if ( icon != null ) { 442 if( !leftToRight ) 443 xOffset -= icon.getIconWidth(); 444 int iconY = ((height / 2) - (icon.getIconHeight() /2)); 445 icon.paintIcon(frame, g, xOffset, iconY); 446 xOffset += leftToRight ? icon.getIconWidth() + 5 : -5; 447 } 448 449 if(frameTitle != null) { 450 Font f = getFont(); 451 g.setFont(f); 452 FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g, f); 453 int fHeight = fm.getHeight(); 454 455 g.setColor(foreground); 456 457 int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent(); 458 459 Rectangle rect = new Rectangle(0, 0, 0, 0); 460 if (frame.isIconifiable()) { rect = iconButton.getBounds(); } 461 else if (frame.isMaximizable()) { rect = maxButton.getBounds(); } 462 else if (frame.isClosable()) { rect = closeButton.getBounds(); } 463 int titleW; 464 465 if( leftToRight ) { 466 if (rect.x == 0) { 467 rect.x = frame.getWidth()-frame.getInsets().right-2; 468 } 469 titleW = rect.x - xOffset - 4; 470 frameTitle = getTitle(frameTitle, fm, titleW); 471 } else { 472 titleW = xOffset - rect.x - rect.width - 4; 473 frameTitle = getTitle(frameTitle, fm, titleW); 474 xOffset -= SwingUtilities2.getTextUIDrawing(frame) 475 .getStringWidth(frame, fm, frameTitle); 476 } 477 478 titleLength = SwingUtilities2.getTextUIDrawing(frame) 479 .getStringWidth(frame, fm, frameTitle); 480 SwingUtilities2.getTextUIDrawing(frame) 481 .drawString(frame, g, frameTitle, xOffset, yOffset); 482 xOffset += leftToRight ? titleLength + 5 : -5; 483 } 484 485 int bumpXOffset; 486 int bumpLength; 487 if( leftToRight ) { 488 bumpLength = width - buttonsWidth - xOffset - 5; 489 bumpXOffset = xOffset; 490 } else { 491 bumpLength = xOffset - buttonsWidth - 5; 492 bumpXOffset = buttonsWidth + 5; 493 } 494 int bumpYOffset = 3; 495 int bumpHeight = getHeight() - (2 * bumpYOffset); 496 bumps.setBumpArea( bumpLength, bumpHeight ); 497 bumps.paintIcon(this, g, bumpXOffset, bumpYOffset); 498 } 499 500 /** 501 * If {@code b} is {@code true}, sets palette icons. 502 * 503 * @param b if {@code true}, sets palette icons 504 */ 505 public void setPalette(boolean b) { 506 isPalette = b; 507 508 if (isPalette) { 509 closeButton.setIcon(paletteCloseIcon); 510 if( frame.isMaximizable() ) 511 remove(maxButton); 512 if( frame.isIconifiable() ) 513 remove(iconButton); 514 } else { 515 closeButton.setIcon(closeIcon); 516 if( frame.isMaximizable() ) 517 add(maxButton); 518 if( frame.isIconifiable() ) 519 add(iconButton); 520 } 521 revalidate(); 522 repaint(); 523 } 524 525 /** 526 * Updates any state dependant upon the JInternalFrame being shown in 527 * a <code>JOptionPane</code>. 528 */ 529 private void updateOptionPaneState() { 530 int type = -2; 531 boolean closable = wasClosable; 532 Object obj = frame.getClientProperty("JInternalFrame.messageType"); 533 534 if (obj == null) { 535 // Don't change the closable state unless in an JOptionPane. 536 return; 537 } 538 if (obj instanceof Integer) { 539 type = ((Integer) obj).intValue(); 540 } 541 switch (type) { 542 case JOptionPane.ERROR_MESSAGE: 543 selectedBackgroundKey = 544 "OptionPane.errorDialog.titlePane.background"; 545 selectedForegroundKey = 546 "OptionPane.errorDialog.titlePane.foreground"; 547 selectedShadowKey = "OptionPane.errorDialog.titlePane.shadow"; 548 closable = false; 549 break; 550 case JOptionPane.QUESTION_MESSAGE: 551 selectedBackgroundKey = 552 "OptionPane.questionDialog.titlePane.background"; 553 selectedForegroundKey = 554 "OptionPane.questionDialog.titlePane.foreground"; 555 selectedShadowKey = 556 "OptionPane.questionDialog.titlePane.shadow"; 557 closable = false; 558 break; 559 case JOptionPane.WARNING_MESSAGE: 560 selectedBackgroundKey = 561 "OptionPane.warningDialog.titlePane.background"; 562 selectedForegroundKey = 563 "OptionPane.warningDialog.titlePane.foreground"; 564 selectedShadowKey = "OptionPane.warningDialog.titlePane.shadow"; 565 closable = false; 566 break; 567 case JOptionPane.INFORMATION_MESSAGE: 568 case JOptionPane.PLAIN_MESSAGE: 569 selectedBackgroundKey = selectedForegroundKey = selectedShadowKey = 570 null; 571 closable = false; 572 break; 573 default: 574 selectedBackgroundKey = selectedForegroundKey = selectedShadowKey = 575 null; 576 break; 577 } 578 if (closable != frame.isClosable()) { 579 frame.setClosable(closable); 580 } 581 } 582 }