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