1 /* 2 * Copyright (c) 2001, 2006, 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 com.sun.java.swing.plaf.windows; 27 28 import sun.swing.SwingUtilities2; 29 30 import javax.swing.*; 31 import javax.swing.border.*; 32 import javax.swing.UIManager; 33 import javax.swing.plaf.*; 34 import javax.swing.plaf.basic.BasicInternalFrameTitlePane; 35 import java.awt.*; 36 import java.awt.event.*; 37 import java.beans.PropertyChangeEvent; 38 import java.beans.PropertyChangeListener; 39 import java.beans.PropertyVetoException; 40 41 import static com.sun.java.swing.plaf.windows.TMSchema.*; 42 import static com.sun.java.swing.plaf.windows.XPStyle.Skin; 43 44 public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane { 45 private Color selectedTitleGradientColor; 46 private Color notSelectedTitleGradientColor; 47 private JPopupMenu systemPopupMenu; 48 private JLabel systemLabel; 49 50 private Font titleFont; 51 private int titlePaneHeight; 52 private int buttonWidth, buttonHeight; 53 private boolean hotTrackingOn; 54 55 public WindowsInternalFrameTitlePane(JInternalFrame f) { 56 super(f); 57 } 58 59 protected void addSubComponents() { 60 add(systemLabel); 61 add(iconButton); 62 add(maxButton); 63 add(closeButton); 64 } 65 66 protected void installDefaults() { 67 super.installDefaults(); 68 69 titlePaneHeight = UIManager.getInt("InternalFrame.titlePaneHeight"); 70 buttonWidth = UIManager.getInt("InternalFrame.titleButtonWidth") - 4; 71 buttonHeight = UIManager.getInt("InternalFrame.titleButtonHeight") - 4; 72 73 Object obj = UIManager.get("InternalFrame.titleButtonToolTipsOn"); 74 hotTrackingOn = (obj instanceof Boolean) ? (Boolean)obj : true; 75 76 77 if (XPStyle.getXP() != null) { 78 // Fix for XP bug where sometimes these sizes aren't updated properly 79 // Assume for now that height is correct and derive width using the 80 // ratio from the uxtheme part 81 buttonWidth = buttonHeight; 82 Dimension d = XPStyle.getPartSize(Part.WP_CLOSEBUTTON, State.NORMAL); 83 if (d != null && d.width != 0 && d.height != 0) { 84 buttonWidth = (int) ((float) buttonWidth * d.width / d.height); 85 } 86 } else { 87 buttonWidth += 2; 88 selectedTitleGradientColor = 89 UIManager.getColor("InternalFrame.activeTitleGradient"); 90 notSelectedTitleGradientColor = 91 UIManager.getColor("InternalFrame.inactiveTitleGradient"); 92 Color activeBorderColor = 93 UIManager.getColor("InternalFrame.activeBorderColor"); 94 setBorder(BorderFactory.createLineBorder(activeBorderColor, 1)); 95 } 96 } 97 98 protected void uninstallListeners() { 99 // Get around protected method in superclass 100 super.uninstallListeners(); 101 } 102 103 protected void createButtons() { 104 super.createButtons(); 105 if (XPStyle.getXP() != null) { 106 iconButton.setContentAreaFilled(false); 107 maxButton.setContentAreaFilled(false); 108 closeButton.setContentAreaFilled(false); 109 } 110 } 111 112 protected void setButtonIcons() { 113 super.setButtonIcons(); 114 115 if (!hotTrackingOn) { 116 iconButton.setToolTipText(null); 117 maxButton.setToolTipText(null); 118 closeButton.setToolTipText(null); 119 } 120 } 121 122 123 public void paintComponent(Graphics g) { 124 XPStyle xp = XPStyle.getXP(); 125 126 paintTitleBackground(g); 127 128 String title = frame.getTitle(); 129 if (title != null) { 130 boolean isSelected = frame.isSelected(); 131 Font oldFont = g.getFont(); 132 Font newFont = (titleFont != null) ? titleFont : getFont(); 133 g.setFont(newFont); 134 135 // Center text vertically. 136 FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g, newFont); 137 int baseline = (getHeight() + fm.getAscent() - fm.getLeading() - 138 fm.getDescent()) / 2; 139 140 int titleX; 141 Rectangle r = new Rectangle(0, 0, 0, 0); 142 if (frame.isIconifiable()) r = iconButton.getBounds(); 143 else if (frame.isMaximizable()) r = maxButton.getBounds(); 144 else if (frame.isClosable()) r = closeButton.getBounds(); 145 int titleW; 146 147 if(WindowsGraphicsUtils.isLeftToRight(frame) ) { 148 if (r.x == 0) r.x = frame.getWidth()-frame.getInsets().right; 149 titleX = systemLabel.getX() + systemLabel.getWidth() + 2; 150 if (xp != null) { 151 titleX += 2; 152 } 153 titleW = r.x - titleX - 3; 154 title = getTitle(frame.getTitle(), fm, titleW); 155 } else { 156 titleX = systemLabel.getX() - 2 157 - SwingUtilities2.stringWidth(frame,fm,title); 158 } 159 if (xp != null) { 160 String shadowType = null; 161 if (isSelected) { 162 shadowType = xp.getString(this, Part.WP_CAPTION, 163 State.ACTIVE, Prop.TEXTSHADOWTYPE); 164 } 165 if ("single".equalsIgnoreCase(shadowType)) { 166 Point shadowOffset = xp.getPoint(this, Part.WP_WINDOW, State.ACTIVE, 167 Prop.TEXTSHADOWOFFSET); 168 Color shadowColor = xp.getColor(this, Part.WP_WINDOW, State.ACTIVE, 169 Prop.TEXTSHADOWCOLOR, null); 170 if (shadowOffset != null && shadowColor != null) { 171 g.setColor(shadowColor); 172 SwingUtilities2.drawString(frame, g, title, 173 titleX + shadowOffset.x, 174 baseline + shadowOffset.y); 175 } 176 } 177 } 178 g.setColor(isSelected ? selectedTextColor : notSelectedTextColor); 179 SwingUtilities2.drawString(frame, g, title, titleX, baseline); 180 g.setFont(oldFont); 181 } 182 } 183 184 public Dimension getPreferredSize() { 185 return getMinimumSize(); 186 } 187 188 public Dimension getMinimumSize() { 189 Dimension d = new Dimension(super.getMinimumSize()); 190 d.height = titlePaneHeight + 2; 191 192 XPStyle xp = XPStyle.getXP(); 193 if (xp != null) { 194 // Note: Don't know how to calculate height on XP, 195 // the captionbarheight is 25 but native caption is 30 (maximized 26) 196 if (frame.isMaximum()) { 197 d.height -= 1; 198 } else { 199 d.height += 3; 200 } 201 } 202 return d; 203 } 204 205 protected void paintTitleBackground(Graphics g) { 206 XPStyle xp = XPStyle.getXP(); 207 if (xp != null) { 208 Part part = frame.isIcon() ? Part.WP_MINCAPTION 209 : (frame.isMaximum() ? Part.WP_MAXCAPTION 210 : Part.WP_CAPTION); 211 State state = frame.isSelected() ? State.ACTIVE : State.INACTIVE; 212 Skin skin = xp.getSkin(this, part); 213 skin.paintSkin(g, 0, 0, getWidth(), getHeight(), state); 214 } else { 215 Boolean gradientsOn = (Boolean)LookAndFeel.getDesktopPropertyValue( 216 "win.frame.captionGradientsOn", Boolean.valueOf(false)); 217 if (gradientsOn.booleanValue() && g instanceof Graphics2D) { 218 Graphics2D g2 = (Graphics2D)g; 219 Paint savePaint = g2.getPaint(); 220 221 boolean isSelected = frame.isSelected(); 222 int w = getWidth(); 223 224 if (isSelected) { 225 GradientPaint titleGradient = new GradientPaint(0,0, 226 selectedTitleColor, 227 (int)(w*.75),0, 228 selectedTitleGradientColor); 229 g2.setPaint(titleGradient); 230 } else { 231 GradientPaint titleGradient = new GradientPaint(0,0, 232 notSelectedTitleColor, 233 (int)(w*.75),0, 234 notSelectedTitleGradientColor); 235 g2.setPaint(titleGradient); 236 } 237 g2.fillRect(0, 0, getWidth(), getHeight()); 238 g2.setPaint(savePaint); 239 } else { 240 super.paintTitleBackground(g); 241 } 242 } 243 } 244 245 protected void assembleSystemMenu() { 246 systemPopupMenu = new JPopupMenu(); 247 addSystemMenuItems(systemPopupMenu); 248 enableActions(); 249 systemLabel = new JLabel(frame.getFrameIcon()) { 250 protected void paintComponent(Graphics g) { 251 int x = 0; 252 int y = 0; 253 int w = getWidth(); 254 int h = getHeight(); 255 g = g.create(); // Create scratch graphics 256 if (isOpaque()) { 257 g.setColor(getBackground()); 258 g.fillRect(0, 0, w, h); 259 } 260 Icon icon = getIcon(); 261 int iconWidth = 0; 262 int iconHeight = 0; 263 if (icon != null && 264 (iconWidth = icon.getIconWidth()) > 0 && 265 (iconHeight = icon.getIconHeight()) > 0) { 266 267 // Set drawing scale to make icon scale to our desired size 268 double drawScale; 269 if (iconWidth > iconHeight) { 270 // Center icon vertically 271 y = (h - w*iconHeight/iconWidth) / 2; 272 drawScale = w / (double)iconWidth; 273 } else { 274 // Center icon horizontally 275 x = (w - h*iconWidth/iconHeight) / 2; 276 drawScale = h / (double)iconHeight; 277 } 278 ((Graphics2D)g).translate(x, y); 279 ((Graphics2D)g).scale(drawScale, drawScale); 280 icon.paintIcon(this, g, 0, 0); 281 } 282 g.dispose(); 283 } 284 }; 285 systemLabel.addMouseListener(new MouseAdapter() { 286 public void mouseClicked(MouseEvent e) { 287 if (e.getClickCount() == 2 && frame.isClosable() && 288 !frame.isIcon()) { 289 systemPopupMenu.setVisible(false); 290 frame.doDefaultCloseAction(); 291 } 292 else { 293 super.mouseClicked(e); 294 } 295 } 296 public void mousePressed(MouseEvent e) { 297 try { 298 frame.setSelected(true); 299 } catch(PropertyVetoException pve) { 300 } 301 showSystemPopupMenu(e.getComponent()); 302 } 303 }); 304 } 305 306 protected void addSystemMenuItems(JPopupMenu menu) { 307 JMenuItem mi = (JMenuItem)menu.add(restoreAction); 308 mi.setMnemonic('R'); 309 mi = (JMenuItem)menu.add(moveAction); 310 mi.setMnemonic('M'); 311 mi = (JMenuItem)menu.add(sizeAction); 312 mi.setMnemonic('S'); 313 mi = (JMenuItem)menu.add(iconifyAction); 314 mi.setMnemonic('n'); 315 mi = (JMenuItem)menu.add(maximizeAction); 316 mi.setMnemonic('x'); 317 systemPopupMenu.add(new JSeparator()); 318 mi = (JMenuItem)menu.add(closeAction); 319 mi.setMnemonic('C'); 320 } 321 322 protected void showSystemMenu(){ 323 showSystemPopupMenu(systemLabel); 324 } 325 326 private void showSystemPopupMenu(Component invoker){ 327 Dimension dim = new Dimension(); 328 Border border = frame.getBorder(); 329 if (border != null) { 330 dim.width += border.getBorderInsets(frame).left + 331 border.getBorderInsets(frame).right; 332 dim.height += border.getBorderInsets(frame).bottom + 333 border.getBorderInsets(frame).top; 334 } 335 if (!frame.isIcon()) { 336 systemPopupMenu.show(invoker, 337 getX() - dim.width, 338 getY() + getHeight() - dim.height); 339 } else { 340 systemPopupMenu.show(invoker, 341 getX() - dim.width, 342 getY() - systemPopupMenu.getPreferredSize().height - 343 dim.height); 344 } 345 } 346 347 protected PropertyChangeListener createPropertyChangeListener() { 348 return new WindowsPropertyChangeHandler(); 349 } 350 351 protected LayoutManager createLayout() { 352 return new WindowsTitlePaneLayout(); 353 } 354 355 public class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout { 356 private Insets captionMargin = null; 357 private Insets contentMargin = null; 358 private XPStyle xp = XPStyle.getXP(); 359 360 WindowsTitlePaneLayout() { 361 if (xp != null) { 362 Component c = WindowsInternalFrameTitlePane.this; 363 captionMargin = xp.getMargin(c, Part.WP_CAPTION, null, Prop.CAPTIONMARGINS); 364 contentMargin = xp.getMargin(c, Part.WP_CAPTION, null, Prop.CONTENTMARGINS); 365 } 366 if (captionMargin == null) { 367 captionMargin = new Insets(0, 2, 0, 2); 368 } 369 if (contentMargin == null) { 370 contentMargin = new Insets(0, 0, 0, 0); 371 } 372 } 373 374 private int layoutButton(JComponent button, Part part, 375 int x, int y, int w, int h, int gap, 376 boolean leftToRight) { 377 if (!leftToRight) { 378 x -= w; 379 } 380 button.setBounds(x, y, w, h); 381 if (leftToRight) { 382 x += w + 2; 383 } else { 384 x -= 2; 385 } 386 return x; 387 } 388 389 public void layoutContainer(Container c) { 390 boolean leftToRight = WindowsGraphicsUtils.isLeftToRight(frame); 391 int x, y; 392 int w = getWidth(); 393 int h = getHeight(); 394 395 // System button 396 // Note: this icon is square, but the buttons aren't always. 397 int iconSize = (xp != null) ? (h-2)*6/10 : h-4; 398 if (xp != null) { 399 x = (leftToRight) ? captionMargin.left + 2 : w - captionMargin.right - 2; 400 } else { 401 x = (leftToRight) ? captionMargin.left : w - captionMargin.right; 402 } 403 y = (h - iconSize) / 2; 404 layoutButton(systemLabel, Part.WP_SYSBUTTON, 405 x, y, iconSize, iconSize, 0, 406 leftToRight); 407 408 // Right hand buttons 409 if (xp != null) { 410 x = (leftToRight) ? w - captionMargin.right - 2 : captionMargin.left + 2; 411 y = 1; // XP seems to ignore margins and offset here 412 if (frame.isMaximum()) { 413 y += 1; 414 } else { 415 y += 5; 416 } 417 } else { 418 x = (leftToRight) ? w - captionMargin.right : captionMargin.left; 419 y = (h - buttonHeight) / 2; 420 } 421 422 if(frame.isClosable()) { 423 x = layoutButton(closeButton, Part.WP_CLOSEBUTTON, 424 x, y, buttonWidth, buttonHeight, 2, 425 !leftToRight); 426 } 427 428 if(frame.isMaximizable()) { 429 x = layoutButton(maxButton, Part.WP_MAXBUTTON, 430 x, y, buttonWidth, buttonHeight, (xp != null) ? 2 : 0, 431 !leftToRight); 432 } 433 434 if(frame.isIconifiable()) { 435 layoutButton(iconButton, Part.WP_MINBUTTON, 436 x, y, buttonWidth, buttonHeight, 0, 437 !leftToRight); 438 } 439 } 440 } // end WindowsTitlePaneLayout 441 442 public class WindowsPropertyChangeHandler extends PropertyChangeHandler { 443 public void propertyChange(PropertyChangeEvent evt) { 444 String prop = (String)evt.getPropertyName(); 445 446 // Update the internal frame icon for the system menu. 447 if (JInternalFrame.FRAME_ICON_PROPERTY.equals(prop) && 448 systemLabel != null) { 449 systemLabel.setIcon(frame.getFrameIcon()); 450 } 451 452 super.propertyChange(evt); 453 } 454 } 455 456 /** 457 * A versatile Icon implementation which can take an array of Icon 458 * instances (typically <code>ImageIcon</code>s) and choose one that gives the best 459 * quality for a given Graphics2D scale factor when painting. 460 * <p> 461 * The class is public so it can be instantiated by UIDefaults.ProxyLazyValue. 462 * <p> 463 * Note: We assume here that icons are square. 464 */ 465 public static class ScalableIconUIResource implements Icon, UIResource { 466 // We can use an arbitrary size here because we scale to it in paintIcon() 467 private static final int SIZE = 16; 468 469 private Icon[] icons; 470 471 /** 472 * @params objects an array of Icon or UIDefaults.LazyValue 473 * <p> 474 * The constructor is public so it can be called by UIDefaults.ProxyLazyValue. 475 */ 476 public ScalableIconUIResource(Object[] objects) { 477 this.icons = new Icon[objects.length]; 478 479 for (int i = 0; i < objects.length; i++) { 480 if (objects[i] instanceof UIDefaults.LazyValue) { 481 icons[i] = (Icon)((UIDefaults.LazyValue)objects[i]).createValue(null); 482 } else { 483 icons[i] = (Icon)objects[i]; 484 } 485 } 486 } 487 488 /** 489 * @return the <code>Icon</code> closest to the requested size 490 */ 491 protected Icon getBestIcon(int size) { 492 if (icons != null && icons.length > 0) { 493 int bestIndex = 0; 494 int minDiff = Integer.MAX_VALUE; 495 for (int i=0; i < icons.length; i++) { 496 Icon icon = icons[i]; 497 int iconSize; 498 if (icon != null && (iconSize = icon.getIconWidth()) > 0) { 499 int diff = Math.abs(iconSize - size); 500 if (diff < minDiff) { 501 minDiff = diff; 502 bestIndex = i; 503 } 504 } 505 } 506 return icons[bestIndex]; 507 } else { 508 return null; 509 } 510 } 511 512 public void paintIcon(Component c, Graphics g, int x, int y) { 513 Graphics2D g2d = (Graphics2D)g.create(); 514 // Calculate how big our drawing area is in pixels 515 // Assume we are square 516 int size = getIconWidth(); 517 double scale = g2d.getTransform().getScaleX(); 518 Icon icon = getBestIcon((int)(size * scale)); 519 int iconSize; 520 if (icon != null && (iconSize = icon.getIconWidth()) > 0) { 521 // Set drawing scale to make icon act true to our reported size 522 double drawScale = size / (double)iconSize; 523 g2d.translate(x, y); 524 g2d.scale(drawScale, drawScale); 525 icon.paintIcon(c, g2d, 0, 0); 526 } 527 g2d.dispose(); 528 } 529 530 public int getIconWidth() { 531 return SIZE; 532 } 533 534 public int getIconHeight() { 535 return SIZE; 536 } 537 } 538 }