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 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 class SynthInternalFrameTitlePane extends BasicInternalFrameTitlePane 46 implements SynthUI, PropertyChangeListener { 47 48 protected JPopupMenu systemPopupMenu; 49 protected JButton menuButton; 50 51 private SynthStyle style; 52 private int titleSpacing; 53 private int buttonSpacing; 54 // Alignment for the title, one of SwingConstants.(LEADING|TRAILING|CENTER) 55 private int titleAlignment; 56 SynthInternalFrameTitlePane(JInternalFrame f)57 public SynthInternalFrameTitlePane(JInternalFrame f) { 58 super(f); 59 } 60 getUIClassID()61 public String getUIClassID() { 62 return "InternalFrameTitlePaneUI"; 63 } 64 getContext(JComponent c)65 public SynthContext getContext(JComponent c) { 66 return getContext(c, getComponentState(c)); 67 } 68 getContext(JComponent c, int state)69 public SynthContext getContext(JComponent c, int state) { 70 return SynthContext.getContext(c, style, state); 71 } 72 getRegion(JComponent c)73 private Region getRegion(JComponent c) { 74 return SynthLookAndFeel.getRegion(c); 75 } 76 getComponentState(JComponent c)77 private int getComponentState(JComponent c) { 78 if (frame != null) { 79 if (frame.isSelected()) { 80 return SELECTED; 81 } 82 } 83 return SynthLookAndFeel.getComponentState(c); 84 } 85 addSubComponents()86 protected void addSubComponents() { 87 menuButton.setName("InternalFrameTitlePane.menuButton"); 88 iconButton.setName("InternalFrameTitlePane.iconifyButton"); 89 maxButton.setName("InternalFrameTitlePane.maximizeButton"); 90 closeButton.setName("InternalFrameTitlePane.closeButton"); 91 92 add(menuButton); 93 add(iconButton); 94 add(maxButton); 95 add(closeButton); 96 } 97 installListeners()98 protected void installListeners() { 99 super.installListeners(); 100 frame.addPropertyChangeListener(this); 101 addPropertyChangeListener(this); 102 } 103 uninstallListeners()104 protected void uninstallListeners() { 105 frame.removePropertyChangeListener(this); 106 removePropertyChangeListener(this); 107 super.uninstallListeners(); 108 } 109 updateStyle(JComponent c)110 private void updateStyle(JComponent c) { 111 SynthContext context = getContext(this, ENABLED); 112 SynthStyle oldStyle = style; 113 style = SynthLookAndFeel.updateStyle(context, this); 114 if (style != oldStyle) { 115 maxIcon = 116 style.getIcon(context,"InternalFrameTitlePane.maximizeIcon"); 117 minIcon = 118 style.getIcon(context,"InternalFrameTitlePane.minimizeIcon"); 119 iconIcon = 120 style.getIcon(context,"InternalFrameTitlePane.iconifyIcon"); 121 closeIcon = 122 style.getIcon(context,"InternalFrameTitlePane.closeIcon"); 123 titleSpacing = style.getInt(context, 124 "InternalFrameTitlePane.titleSpacing", 2); 125 buttonSpacing = style.getInt(context, 126 "InternalFrameTitlePane.buttonSpacing", 2); 127 String alignString = (String)style.get(context, 128 "InternalFrameTitlePane.titleAlignment"); 129 titleAlignment = SwingConstants.LEADING; 130 if (alignString != null) { 131 alignString = alignString.toUpperCase(); 132 if (alignString.equals("TRAILING")) { 133 titleAlignment = SwingConstants.TRAILING; 134 } 135 else if (alignString.equals("CENTER")) { 136 titleAlignment = SwingConstants.CENTER; 137 } 138 } 139 } 140 context.dispose(); 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 context.dispose(); 152 style = null; 153 JInternalFrame.JDesktopIcon di = frame.getDesktopIcon(); 154 if(di != null && di.getComponentPopupMenu() == systemPopupMenu) { 155 // Release link to systemMenu from the JInternalFrame 156 di.setComponentPopupMenu(null); 157 } 158 super.uninstallDefaults(); 159 } 160 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 context.dispose(); 237 } 238 paint(SynthContext context, Graphics g)239 protected void paint(SynthContext context, Graphics g) { 240 String title = frame.getTitle(); 241 242 if (title != null) { 243 SynthStyle style = context.getStyle(); 244 245 g.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND)); 246 g.setFont(style.getFont(context)); 247 248 // Center text vertically. 249 FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g); 250 int baseline = (getHeight() + fm.getAscent() - fm.getLeading() - 251 fm.getDescent()) / 2; 252 JButton lastButton = null; 253 if (frame.isIconifiable()) { 254 lastButton = iconButton; 255 } 256 else if (frame.isMaximizable()) { 257 lastButton = maxButton; 258 } 259 else if (frame.isClosable()) { 260 lastButton = closeButton; 261 } 262 int maxX; 263 int minX; 264 boolean ltr = SynthLookAndFeel.isLeftToRight(frame); 265 int titleAlignment = this.titleAlignment; 266 if (ltr) { 267 if (lastButton != null) { 268 maxX = lastButton.getX() - titleSpacing; 269 } 270 else { 271 maxX = frame.getWidth() - frame.getInsets().right - 272 titleSpacing; 273 } 274 minX = menuButton.getX() + menuButton.getWidth() + 275 titleSpacing; 276 } 277 else { 278 if (lastButton != null) { 279 minX = lastButton.getX() + lastButton.getWidth() + 280 titleSpacing; 281 } 282 else { 283 minX = frame.getInsets().left + titleSpacing; 284 } 285 maxX = menuButton.getX() - titleSpacing; 286 if (titleAlignment == SwingConstants.LEADING) { 287 titleAlignment = SwingConstants.TRAILING; 288 } 289 else if (titleAlignment == SwingConstants.TRAILING) { 290 titleAlignment = SwingConstants.LEADING; 291 } 292 } 293 String clippedTitle = getTitle(title, fm, maxX - minX); 294 if (clippedTitle == title) { 295 // String fit, align as necessary. 296 if (titleAlignment == SwingConstants.TRAILING) { 297 minX = maxX - style.getGraphicsUtils(context). 298 computeStringWidth(context, g.getFont(), fm, title); 299 } 300 else if (titleAlignment == SwingConstants.CENTER) { 301 int width = style.getGraphicsUtils(context). 302 computeStringWidth(context, g.getFont(), fm, title); 303 minX = Math.max(minX, (getWidth() - width) / 2); 304 minX = Math.min(maxX - width, minX); 305 } 306 } 307 style.getGraphicsUtils(context).paintText( 308 context, g, clippedTitle, minX, baseline - fm.getAscent(), -1); 309 } 310 } 311 paintBorder(SynthContext context, Graphics g, int x, int y, int w, int h)312 public void paintBorder(SynthContext context, Graphics g, int x, 313 int y, int w, int h) { 314 context.getPainter().paintInternalFrameTitlePaneBorder(context, 315 g, x, y, w, h); 316 } 317 createLayout()318 protected LayoutManager createLayout() { 319 SynthContext context = getContext(this); 320 LayoutManager lm = 321 (LayoutManager)style.get(context, "InternalFrameTitlePane.titlePaneLayout"); 322 context.dispose(); 323 return (lm != null) ? lm : new SynthTitlePaneLayout(); 324 } 325 propertyChange(PropertyChangeEvent evt)326 public void propertyChange(PropertyChangeEvent evt) { 327 if (evt.getSource() == this) { 328 if (SynthLookAndFeel.shouldUpdateStyle(evt)) { 329 updateStyle(this); 330 } 331 } 332 else { 333 // Changes for the internal frame 334 if (evt.getPropertyName() == JInternalFrame.FRAME_ICON_PROPERTY) { 335 updateMenuIcon(); 336 } 337 } 338 } 339 340 /** 341 * Resets the menuButton icon to match that of the frame. 342 */ updateMenuIcon()343 private void updateMenuIcon() { 344 Icon frameIcon = frame.getFrameIcon(); 345 SynthContext context = getContext(this); 346 if (frameIcon != null) { 347 Dimension maxSize = (Dimension)context.getStyle().get(context, 348 "InternalFrameTitlePane.maxFrameIconSize"); 349 int maxWidth = 16; 350 int maxHeight = 16; 351 if (maxSize != null) { 352 maxWidth = maxSize.width; 353 maxHeight = maxSize.height; 354 } 355 if ((frameIcon.getIconWidth() > maxWidth || 356 frameIcon.getIconHeight() > maxHeight) && 357 (frameIcon instanceof ImageIcon)) { 358 frameIcon = new ImageIcon(((ImageIcon)frameIcon). 359 getImage().getScaledInstance(maxWidth, maxHeight, 360 Image.SCALE_SMOOTH)); 361 } 362 } 363 context.dispose(); 364 menuButton.setIcon(frameIcon); 365 } 366 367 368 class SynthTitlePaneLayout implements LayoutManager { addLayoutComponent(String name, Component c)369 public void addLayoutComponent(String name, Component c) {} removeLayoutComponent(Component c)370 public void removeLayoutComponent(Component c) {} preferredLayoutSize(Container c)371 public Dimension preferredLayoutSize(Container c) { 372 return minimumLayoutSize(c); 373 } 374 minimumLayoutSize(Container c)375 public Dimension minimumLayoutSize(Container c) { 376 SynthContext context = getContext( 377 SynthInternalFrameTitlePane.this); 378 int width = 0; 379 int height = 0; 380 381 int buttonCount = 0; 382 Dimension pref; 383 384 if (frame.isClosable()) { 385 pref = closeButton.getPreferredSize(); 386 width += pref.width; 387 height = Math.max(pref.height, height); 388 buttonCount++; 389 } 390 if (frame.isMaximizable()) { 391 pref = maxButton.getPreferredSize(); 392 width += pref.width; 393 height = Math.max(pref.height, height); 394 buttonCount++; 395 } 396 if (frame.isIconifiable()) { 397 pref = iconButton.getPreferredSize(); 398 width += pref.width; 399 height = Math.max(pref.height, height); 400 buttonCount++; 401 } 402 pref = menuButton.getPreferredSize(); 403 width += pref.width; 404 height = Math.max(pref.height, height); 405 406 width += Math.max(0, (buttonCount - 1) * buttonSpacing); 407 408 FontMetrics fm = SynthInternalFrameTitlePane.this.getFontMetrics( 409 getFont()); 410 SynthGraphicsUtils graphicsUtils = context.getStyle(). 411 getGraphicsUtils(context); 412 String frameTitle = frame.getTitle(); 413 int title_w = frameTitle != null ? graphicsUtils. 414 computeStringWidth(context, fm.getFont(), 415 fm, frameTitle) : 0; 416 int title_length = frameTitle != null ? frameTitle.length() : 0; 417 418 // Leave room for three characters in the title. 419 if (title_length > 3) { 420 int subtitle_w = graphicsUtils.computeStringWidth(context, 421 fm.getFont(), fm, frameTitle.substring(0, 3) + "..."); 422 width += (title_w < subtitle_w) ? title_w : subtitle_w; 423 } else { 424 width += title_w; 425 } 426 427 height = Math.max(fm.getHeight() + 2, height); 428 429 width += titleSpacing + titleSpacing; 430 431 Insets insets = getInsets(); 432 height += insets.top + insets.bottom; 433 width += insets.left + insets.right; 434 context.dispose(); 435 return new Dimension(width, height); 436 } 437 center(Component c, Insets insets, int x, boolean trailing)438 private int center(Component c, Insets insets, int x, 439 boolean trailing) { 440 Dimension pref = c.getPreferredSize(); 441 if (trailing) { 442 x -= pref.width; 443 } 444 c.setBounds(x, insets.top + 445 (getHeight() - insets.top - insets.bottom - 446 pref.height) / 2, pref.width, pref.height); 447 if (pref.width > 0) { 448 if (trailing) { 449 return x - buttonSpacing; 450 } 451 return x + pref.width + buttonSpacing; 452 } 453 return x; 454 } 455 layoutContainer(Container c)456 public void layoutContainer(Container c) { 457 Insets insets = c.getInsets(); 458 Dimension pref; 459 460 if (SynthLookAndFeel.isLeftToRight(frame)) { 461 center(menuButton, insets, insets.left, false); 462 int x = getWidth() - insets.right; 463 if (frame.isClosable()) { 464 x = center(closeButton, insets, x, true); 465 } 466 if (frame.isMaximizable()) { 467 x = center(maxButton, insets, x, true); 468 } 469 if (frame.isIconifiable()) { 470 x = center(iconButton, insets, x, true); 471 } 472 } 473 else { 474 center(menuButton, insets, getWidth() - insets.right, 475 true); 476 int x = insets.left; 477 if (frame.isClosable()) { 478 x = center(closeButton, insets, x, false); 479 } 480 if (frame.isMaximizable()) { 481 x = center(maxButton, insets, x, false); 482 } 483 if (frame.isIconifiable()) { 484 x = center(iconButton, insets, x, false); 485 } 486 } 487 } 488 } 489 createNoFocusButton()490 private JButton createNoFocusButton() { 491 JButton button = new JButton(); 492 button.setFocusable(false); 493 button.setMargin(new Insets(0,0,0,0)); 494 return button; 495 } 496 } 497