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