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