1 /* 2 * Copyright (c) 2002, 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.synth; 27 28 import java.awt.*; 29 import java.awt.event.*; 30 import javax.swing.*; 31 import javax.swing.plaf.*; 32 import javax.swing.plaf.basic.BasicInternalFrameTitlePane; 33 import java.beans.PropertyChangeListener; 34 import java.beans.PropertyChangeEvent; 35 import java.beans.PropertyVetoException; 36 import sun.swing.SwingUtilities2; 37 38 /** 39 * The class that manages a synth title bar 40 * 41 * @author David Kloba 42 * @author Joshua Outwater 43 * @author Steve Wilson 44 */ 45 @SuppressWarnings("serial") // Superclass is not serializable across versions 46 class SynthInternalFrameTitlePane extends BasicInternalFrameTitlePane 47 implements SynthUI, PropertyChangeListener { 48 49 protected JPopupMenu systemPopupMenu; 50 protected JButton menuButton; 51 52 private SynthStyle style; 53 private int titleSpacing; 54 private int buttonSpacing; 55 // Alignment for the title, one of SwingConstants.(LEADING|TRAILING|CENTER) 56 private int titleAlignment; 57 SynthInternalFrameTitlePane(JInternalFrame f)58 public SynthInternalFrameTitlePane(JInternalFrame f) { 59 super(f); 60 } 61 getUIClassID()62 public String getUIClassID() { 63 return "InternalFrameTitlePaneUI"; 64 } 65 getContext(JComponent c)66 public SynthContext getContext(JComponent c) { 67 return getContext(c, getComponentState(c)); 68 } 69 getContext(JComponent c, int state)70 public SynthContext getContext(JComponent c, int state) { 71 return SynthContext.getContext(c, style, state); 72 } 73 getRegion(JComponent c)74 private Region getRegion(JComponent c) { 75 return SynthLookAndFeel.getRegion(c); 76 } 77 getComponentState(JComponent c)78 private int getComponentState(JComponent c) { 79 if (frame != null) { 80 if (frame.isSelected()) { 81 return SELECTED; 82 } 83 } 84 return SynthLookAndFeel.getComponentState(c); 85 } 86 addSubComponents()87 protected void addSubComponents() { 88 menuButton.setName("InternalFrameTitlePane.menuButton"); 89 iconButton.setName("InternalFrameTitlePane.iconifyButton"); 90 maxButton.setName("InternalFrameTitlePane.maximizeButton"); 91 closeButton.setName("InternalFrameTitlePane.closeButton"); 92 93 add(menuButton); 94 add(iconButton); 95 add(maxButton); 96 add(closeButton); 97 } 98 installListeners()99 protected void installListeners() { 100 super.installListeners(); 101 frame.addPropertyChangeListener(this); 102 addPropertyChangeListener(this); 103 } 104 uninstallListeners()105 protected void uninstallListeners() { 106 frame.removePropertyChangeListener(this); 107 removePropertyChangeListener(this); 108 super.uninstallListeners(); 109 } 110 updateStyle(JComponent c)111 private void updateStyle(JComponent c) { 112 SynthContext context = getContext(this, ENABLED); 113 SynthStyle oldStyle = style; 114 style = SynthLookAndFeel.updateStyle(context, this); 115 if (style != oldStyle) { 116 maxIcon = 117 style.getIcon(context,"InternalFrameTitlePane.maximizeIcon"); 118 minIcon = 119 style.getIcon(context,"InternalFrameTitlePane.minimizeIcon"); 120 iconIcon = 121 style.getIcon(context,"InternalFrameTitlePane.iconifyIcon"); 122 closeIcon = 123 style.getIcon(context,"InternalFrameTitlePane.closeIcon"); 124 titleSpacing = style.getInt(context, 125 "InternalFrameTitlePane.titleSpacing", 2); 126 buttonSpacing = style.getInt(context, 127 "InternalFrameTitlePane.buttonSpacing", 2); 128 String alignString = (String)style.get(context, 129 "InternalFrameTitlePane.titleAlignment"); 130 titleAlignment = SwingConstants.LEADING; 131 if (alignString != null) { 132 alignString = alignString.toUpperCase(); 133 if (alignString.equals("TRAILING")) { 134 titleAlignment = SwingConstants.TRAILING; 135 } 136 else if (alignString.equals("CENTER")) { 137 titleAlignment = SwingConstants.CENTER; 138 } 139 } 140 } 141 } 142 installDefaults()143 protected void installDefaults() { 144 super.installDefaults(); 145 updateStyle(this); 146 } 147 uninstallDefaults()148 protected void uninstallDefaults() { 149 SynthContext context = getContext(this, ENABLED); 150 style.uninstallDefaults(context); 151 style = null; 152 JInternalFrame.JDesktopIcon di = frame.getDesktopIcon(); 153 if(di != null && di.getComponentPopupMenu() == systemPopupMenu) { 154 // Release link to systemMenu from the JInternalFrame 155 di.setComponentPopupMenu(null); 156 } 157 super.uninstallDefaults(); 158 } 159 160 @SuppressWarnings("serial") // Superclass is not serializable across versions 161 private static class JPopupMenuUIResource extends JPopupMenu implements 162 UIResource { } 163 assembleSystemMenu()164 protected void assembleSystemMenu() { 165 systemPopupMenu = new JPopupMenuUIResource(); 166 addSystemMenuItems(systemPopupMenu); 167 enableActions(); 168 menuButton = createNoFocusButton(); 169 updateMenuIcon(); 170 menuButton.addMouseListener(new MouseAdapter() { 171 public void mousePressed(MouseEvent e) { 172 try { 173 frame.setSelected(true); 174 } catch(PropertyVetoException pve) { 175 } 176 showSystemMenu(); 177 } 178 }); 179 JPopupMenu p = frame.getComponentPopupMenu(); 180 if (p == null || p instanceof UIResource) { 181 frame.setComponentPopupMenu(systemPopupMenu); 182 } 183 if (frame.getDesktopIcon() != null) { 184 p = frame.getDesktopIcon().getComponentPopupMenu(); 185 if (p == null || p instanceof UIResource) { 186 frame.getDesktopIcon().setComponentPopupMenu(systemPopupMenu); 187 } 188 } 189 setInheritsPopupMenu(true); 190 } 191 addSystemMenuItems(JPopupMenu menu)192 protected void addSystemMenuItems(JPopupMenu menu) { 193 JMenuItem mi = menu.add(restoreAction); 194 mi.setMnemonic(getButtonMnemonic("restore")); 195 mi = menu.add(moveAction); 196 mi.setMnemonic(getButtonMnemonic("move")); 197 mi = menu.add(sizeAction); 198 mi.setMnemonic(getButtonMnemonic("size")); 199 mi = menu.add(iconifyAction); 200 mi.setMnemonic(getButtonMnemonic("minimize")); 201 mi = menu.add(maximizeAction); 202 mi.setMnemonic(getButtonMnemonic("maximize")); 203 menu.add(new JSeparator()); 204 mi = menu.add(closeAction); 205 mi.setMnemonic(getButtonMnemonic("close")); 206 } 207 getButtonMnemonic(String button)208 private static int getButtonMnemonic(String button) { 209 try { 210 return Integer.parseInt(UIManager.getString( 211 "InternalFrameTitlePane." + button + "Button.mnemonic")); 212 } catch (NumberFormatException e) { 213 return -1; 214 } 215 } 216 showSystemMenu()217 protected void showSystemMenu() { 218 Insets insets = frame.getInsets(); 219 if (!frame.isIcon()) { 220 systemPopupMenu.show(frame, menuButton.getX(), getY() + getHeight()); 221 } else { 222 systemPopupMenu.show(menuButton, 223 getX() - insets.left - insets.right, 224 getY() - systemPopupMenu.getPreferredSize().height - 225 insets.bottom - insets.top); 226 } 227 } 228 229 // SynthInternalFrameTitlePane has no UI, we'll invoke paint on it. paintComponent(Graphics g)230 public void paintComponent(Graphics g) { 231 SynthContext context = getContext(this); 232 SynthLookAndFeel.update(context, g); 233 context.getPainter().paintInternalFrameTitlePaneBackground(context, 234 g, 0, 0, getWidth(), getHeight()); 235 paint(context, g); 236 } 237 paint(SynthContext context, Graphics g)238 protected void paint(SynthContext context, Graphics g) { 239 String title = frame.getTitle(); 240 241 if (title != null) { 242 SynthStyle style = context.getStyle(); 243 244 g.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND)); 245 g.setFont(style.getFont(context)); 246 247 // Center text vertically. 248 FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g); 249 int baseline = (getHeight() + fm.getAscent() - fm.getLeading() - 250 fm.getDescent()) / 2; 251 JButton lastButton = null; 252 if (frame.isIconifiable()) { 253 lastButton = iconButton; 254 } 255 else if (frame.isMaximizable()) { 256 lastButton = maxButton; 257 } 258 else if (frame.isClosable()) { 259 lastButton = closeButton; 260 } 261 int maxX; 262 int minX; 263 boolean ltr = SynthLookAndFeel.isLeftToRight(frame); 264 int titleAlignment = this.titleAlignment; 265 if (ltr) { 266 if (lastButton != null) { 267 maxX = lastButton.getX() - titleSpacing; 268 } 269 else { 270 maxX = frame.getWidth() - frame.getInsets().right - 271 titleSpacing; 272 } 273 minX = menuButton.getX() + menuButton.getWidth() + 274 titleSpacing; 275 } 276 else { 277 if (lastButton != null) { 278 minX = lastButton.getX() + lastButton.getWidth() + 279 titleSpacing; 280 } 281 else { 282 minX = frame.getInsets().left + titleSpacing; 283 } 284 maxX = menuButton.getX() - titleSpacing; 285 if (titleAlignment == SwingConstants.LEADING) { 286 titleAlignment = SwingConstants.TRAILING; 287 } 288 else if (titleAlignment == SwingConstants.TRAILING) { 289 titleAlignment = SwingConstants.LEADING; 290 } 291 } 292 String clippedTitle = getTitle(title, fm, maxX - minX); 293 if (clippedTitle == title) { 294 // String fit, align as necessary. 295 if (titleAlignment == SwingConstants.TRAILING) { 296 minX = maxX - style.getGraphicsUtils(context). 297 computeStringWidth(context, g.getFont(), fm, title); 298 } 299 else if (titleAlignment == SwingConstants.CENTER) { 300 int width = style.getGraphicsUtils(context). 301 computeStringWidth(context, g.getFont(), fm, title); 302 minX = Math.max(minX, (getWidth() - width) / 2); 303 minX = Math.min(maxX - width, minX); 304 } 305 } 306 style.getGraphicsUtils(context).paintText( 307 context, g, clippedTitle, minX, baseline - fm.getAscent(), -1); 308 } 309 } 310 paintBorder(SynthContext context, Graphics g, int x, int y, int w, int h)311 public void paintBorder(SynthContext context, Graphics g, int x, 312 int y, int w, int h) { 313 context.getPainter().paintInternalFrameTitlePaneBorder(context, 314 g, x, y, w, h); 315 } 316 createLayout()317 protected LayoutManager createLayout() { 318 SynthContext context = getContext(this); 319 LayoutManager lm = 320 (LayoutManager)style.get(context, "InternalFrameTitlePane.titlePaneLayout"); 321 return (lm != null) ? lm : new SynthTitlePaneLayout(); 322 } 323 propertyChange(PropertyChangeEvent evt)324 public void propertyChange(PropertyChangeEvent evt) { 325 if (evt.getSource() == this) { 326 if (SynthLookAndFeel.shouldUpdateStyle(evt)) { 327 updateStyle(this); 328 } 329 } 330 else { 331 // Changes for the internal frame 332 if (evt.getPropertyName() == JInternalFrame.FRAME_ICON_PROPERTY) { 333 updateMenuIcon(); 334 } 335 } 336 } 337 338 /** 339 * Resets the menuButton icon to match that of the frame. 340 */ updateMenuIcon()341 private void updateMenuIcon() { 342 Icon frameIcon = frame.getFrameIcon(); 343 SynthContext context = getContext(this); 344 if (frameIcon != null) { 345 Dimension maxSize = (Dimension)context.getStyle().get(context, 346 "InternalFrameTitlePane.maxFrameIconSize"); 347 int maxWidth = 16; 348 int maxHeight = 16; 349 if (maxSize != null) { 350 maxWidth = maxSize.width; 351 maxHeight = maxSize.height; 352 } 353 if ((frameIcon.getIconWidth() > maxWidth || 354 frameIcon.getIconHeight() > maxHeight) && 355 (frameIcon instanceof ImageIcon)) { 356 frameIcon = new ImageIcon(((ImageIcon)frameIcon). 357 getImage().getScaledInstance(maxWidth, maxHeight, 358 Image.SCALE_SMOOTH)); 359 } 360 } 361 menuButton.setIcon(frameIcon); 362 } 363 364 365 class SynthTitlePaneLayout implements LayoutManager { addLayoutComponent(String name, Component c)366 public void addLayoutComponent(String name, Component c) {} removeLayoutComponent(Component c)367 public void removeLayoutComponent(Component c) {} preferredLayoutSize(Container c)368 public Dimension preferredLayoutSize(Container c) { 369 return minimumLayoutSize(c); 370 } 371 minimumLayoutSize(Container c)372 public Dimension minimumLayoutSize(Container c) { 373 SynthContext context = getContext( 374 SynthInternalFrameTitlePane.this); 375 int width = 0; 376 int height = 0; 377 378 int buttonCount = 0; 379 Dimension pref; 380 381 if (frame.isClosable()) { 382 pref = closeButton.getPreferredSize(); 383 width += pref.width; 384 height = Math.max(pref.height, height); 385 buttonCount++; 386 } 387 if (frame.isMaximizable()) { 388 pref = maxButton.getPreferredSize(); 389 width += pref.width; 390 height = Math.max(pref.height, height); 391 buttonCount++; 392 } 393 if (frame.isIconifiable()) { 394 pref = iconButton.getPreferredSize(); 395 width += pref.width; 396 height = Math.max(pref.height, height); 397 buttonCount++; 398 } 399 pref = menuButton.getPreferredSize(); 400 width += pref.width; 401 height = Math.max(pref.height, height); 402 403 width += Math.max(0, (buttonCount - 1) * buttonSpacing); 404 405 FontMetrics fm = SynthInternalFrameTitlePane.this.getFontMetrics( 406 getFont()); 407 SynthGraphicsUtils graphicsUtils = context.getStyle(). 408 getGraphicsUtils(context); 409 String frameTitle = frame.getTitle(); 410 int title_w = frameTitle != null ? graphicsUtils. 411 computeStringWidth(context, fm.getFont(), 412 fm, frameTitle) : 0; 413 int title_length = frameTitle != null ? frameTitle.length() : 0; 414 415 // Leave room for three characters in the title. 416 if (title_length > 3) { 417 int subtitle_w = graphicsUtils.computeStringWidth(context, 418 fm.getFont(), fm, frameTitle.substring(0, 3) + "..."); 419 width += (title_w < subtitle_w) ? title_w : subtitle_w; 420 } else { 421 width += title_w; 422 } 423 424 height = Math.max(fm.getHeight() + 2, height); 425 426 width += titleSpacing + titleSpacing; 427 428 Insets insets = getInsets(); 429 height += insets.top + insets.bottom; 430 width += insets.left + insets.right; 431 return new Dimension(width, height); 432 } 433 center(Component c, Insets insets, int x, boolean trailing)434 private int center(Component c, Insets insets, int x, 435 boolean trailing) { 436 Dimension pref = c.getPreferredSize(); 437 if (trailing) { 438 x -= pref.width; 439 } 440 c.setBounds(x, insets.top + 441 (getHeight() - insets.top - insets.bottom - 442 pref.height) / 2, pref.width, pref.height); 443 if (pref.width > 0) { 444 if (trailing) { 445 return x - buttonSpacing; 446 } 447 return x + pref.width + buttonSpacing; 448 } 449 return x; 450 } 451 layoutContainer(Container c)452 public void layoutContainer(Container c) { 453 Insets insets = c.getInsets(); 454 Dimension pref; 455 456 if (SynthLookAndFeel.isLeftToRight(frame)) { 457 center(menuButton, insets, insets.left, false); 458 int x = getWidth() - insets.right; 459 if (frame.isClosable()) { 460 x = center(closeButton, insets, x, true); 461 } 462 if (frame.isMaximizable()) { 463 x = center(maxButton, insets, x, true); 464 } 465 if (frame.isIconifiable()) { 466 x = center(iconButton, insets, x, true); 467 } 468 } 469 else { 470 center(menuButton, insets, getWidth() - insets.right, 471 true); 472 int x = insets.left; 473 if (frame.isClosable()) { 474 x = center(closeButton, insets, x, false); 475 } 476 if (frame.isMaximizable()) { 477 x = center(maxButton, insets, x, false); 478 } 479 if (frame.isIconifiable()) { 480 x = center(iconButton, insets, x, false); 481 } 482 } 483 } 484 } 485 createNoFocusButton()486 private JButton createNoFocusButton() { 487 JButton button = new JButton(); 488 button.setFocusable(false); 489 button.setMargin(new Insets(0,0,0,0)); 490 return button; 491 } 492 } 493