001 /*
002 * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.swing.plaf.synth;
027
028 import java.awt.*;
029 import java.awt.event.*;
030 import javax.swing.*;
031 import javax.swing.plaf.*;
032 import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
033 import javax.swing.border.*;
034 import javax.swing.event.InternalFrameEvent;
035 import java.util.EventListener;
036 import java.beans.PropertyChangeListener;
037 import java.beans.PropertyChangeEvent;
038 import java.beans.VetoableChangeListener;
039 import java.beans.PropertyVetoException;
040 import sun.swing.plaf.synth.SynthUI;
041 import sun.swing.SwingUtilities2;
042
043 /**
044 * The class that manages a synth title bar
045 *
046 * @version 1.33 05/05/07
047 * @author David Kloba
048 * @author Joshua Outwater
049 * @author Steve Wilson
050 */
051 class SynthInternalFrameTitlePane extends BasicInternalFrameTitlePane
052 implements SynthUI, PropertyChangeListener {
053
054 protected JPopupMenu systemPopupMenu;
055 protected JButton menuButton;
056
057 private SynthStyle style;
058 private int titleSpacing;
059 private int buttonSpacing;
060 // Alignment for the title, one of SwingConstants.(LEADING|TRAILING|CENTER)
061 private int titleAlignment;
062
063 public SynthInternalFrameTitlePane(JInternalFrame f) {
064 super (f);
065 }
066
067 public String getUIClassID() {
068 return "InternalFrameTitlePaneUI";
069 }
070
071 public SynthContext getContext(JComponent c) {
072 return getContext(c, getComponentState(c));
073 }
074
075 public SynthContext getContext(JComponent c, int state) {
076 return SynthContext.getContext(SynthContext.class, c,
077 SynthLookAndFeel.getRegion(c), style, state);
078 }
079
080 private Region getRegion(JComponent c) {
081 return SynthLookAndFeel.getRegion(c);
082 }
083
084 private int getComponentState(JComponent c) {
085 if (frame != null) {
086 if (frame.isSelected()) {
087 return SELECTED;
088 }
089 }
090 return SynthLookAndFeel.getComponentState(c);
091 }
092
093 protected void addSubComponents() {
094 menuButton.setName("InternalFrameTitlePane.menuButton");
095 iconButton.setName("InternalFrameTitlePane.iconifyButton");
096 maxButton.setName("InternalFrameTitlePane.maximizeButton");
097 closeButton.setName("InternalFrameTitlePane.closeButton");
098
099 add(menuButton);
100 add(iconButton);
101 add(maxButton);
102 add(closeButton);
103 }
104
105 protected void installListeners() {
106 super .installListeners();
107 frame.addPropertyChangeListener(this );
108 addPropertyChangeListener(this );
109 }
110
111 protected void uninstallListeners() {
112 frame.removePropertyChangeListener(this );
113 removePropertyChangeListener(this );
114 super .uninstallListeners();
115 }
116
117 private void updateStyle(JComponent c) {
118 SynthContext context = getContext(this , ENABLED);
119 SynthStyle oldStyle = style;
120 style = SynthLookAndFeel.updateStyle(context, this );
121 if (style != oldStyle) {
122 maxIcon = style.getIcon(context,
123 "InternalFrameTitlePane.maximizeIcon");
124 minIcon = style.getIcon(context,
125 "InternalFrameTitlePane.minimizeIcon");
126 iconIcon = style.getIcon(context,
127 "InternalFrameTitlePane.iconifyIcon");
128 closeIcon = style.getIcon(context,
129 "InternalFrameTitlePane.closeIcon");
130 titleSpacing = style.getInt(context,
131 "InternalFrameTitlePane.titleSpacing", 2);
132 buttonSpacing = style.getInt(context,
133 "InternalFrameTitlePane.buttonSpacing", 2);
134 String alignString = (String) style.get(context,
135 "InternalFrameTitlePane.titleAlignment");
136 titleAlignment = SwingConstants.LEADING;
137 if (alignString != null) {
138 alignString = alignString.toUpperCase();
139 if (alignString.equals("TRAILING")) {
140 titleAlignment = SwingConstants.TRAILING;
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
168 implements UIResource {
169 }
170
171 protected void assembleSystemMenu() {
172 systemPopupMenu = new JPopupMenuUIResource();
173 addSystemMenuItems(systemPopupMenu);
174 enableActions();
175 menuButton = createNoFocusButton();
176 updateMenuIcon();
177 menuButton.addMouseListener(new MouseAdapter() {
178 public void mousePressed(MouseEvent e) {
179 try {
180 frame.setSelected(true);
181 } catch (PropertyVetoException pve) {
182 }
183 showSystemMenu();
184 }
185 });
186 JPopupMenu p = frame.getComponentPopupMenu();
187 if (p == null || p instanceof UIResource) {
188 frame.setComponentPopupMenu(systemPopupMenu);
189 }
190 if (frame.getDesktopIcon() != null) {
191 p = frame.getDesktopIcon().getComponentPopupMenu();
192 if (p == null || p instanceof UIResource) {
193 frame.getDesktopIcon().setComponentPopupMenu(
194 systemPopupMenu);
195 }
196 }
197 setInheritsPopupMenu(true);
198 }
199
200 protected void addSystemMenuItems(JPopupMenu menu) {
201 // PENDING: this should all be localizable!
202 JMenuItem mi = (JMenuItem) menu.add(restoreAction);
203 mi.setMnemonic('R');
204 mi = (JMenuItem) menu.add(moveAction);
205 mi.setMnemonic('M');
206 mi = (JMenuItem) menu.add(sizeAction);
207 mi.setMnemonic('S');
208 mi = (JMenuItem) menu.add(iconifyAction);
209 mi.setMnemonic('n');
210 mi = (JMenuItem) menu.add(maximizeAction);
211 mi.setMnemonic('x');
212 menu.add(new JSeparator());
213 mi = (JMenuItem) menu.add(closeAction);
214 mi.setMnemonic('C');
215 }
216
217 protected void showSystemMenu() {
218 Insets insets = frame.getInsets();
219 if (!frame.isIcon()) {
220 systemPopupMenu.show(frame, insets.left, getY()
221 + getHeight());
222 } else {
223 systemPopupMenu.show(menuButton, getX() - insets.left
224 - insets.right, getY()
225 - 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(
235 context, 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,
247 ColorType.TEXT_FOREGROUND));
248 g.setFont(style.getFont(context));
249
250 // Center text vertically.
251 FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g);
252 int baseline = (getHeight() + fm.getAscent()
253 - fm.getLeading() - fm.getDescent()) / 2;
254 JButton lastButton = null;
255 if (frame.isIconifiable()) {
256 lastButton = iconButton;
257 } else if (frame.isMaximizable()) {
258 lastButton = maxButton;
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 } else {
270 maxX = frame.getWidth() - frame.getInsets().right
271 - titleSpacing;
272 }
273 minX = menuButton.getX() + menuButton.getWidth()
274 + titleSpacing;
275 } else {
276 if (lastButton != null) {
277 minX = lastButton.getX() + lastButton.getWidth()
278 + titleSpacing;
279 } else {
280 minX = frame.getInsets().left + titleSpacing;
281 }
282 maxX = menuButton.getX() - titleSpacing;
283 if (titleAlignment == SwingConstants.LEADING) {
284 titleAlignment = SwingConstants.TRAILING;
285 } else if (titleAlignment == SwingConstants.TRAILING) {
286 titleAlignment = SwingConstants.LEADING;
287 }
288 }
289 String clippedTitle = getTitle(title, fm, maxX - minX);
290 if (clippedTitle == title) {
291 // String fit, align as necessary.
292 if (titleAlignment == SwingConstants.TRAILING) {
293 minX = maxX
294 - style.getGraphicsUtils(context)
295 .computeStringWidth(context,
296 g.getFont(), fm, title);
297 } else if (titleAlignment == SwingConstants.CENTER) {
298 int width = style.getGraphicsUtils(context)
299 .computeStringWidth(context, g.getFont(),
300 fm, title);
301 minX = Math.max(minX, (getWidth() - width) / 2);
302 minX = Math.min(maxX - width, minX);
303 }
304 }
305 style.getGraphicsUtils(context).paintText(context, g,
306 clippedTitle, minX, baseline - fm.getAscent(), -1);
307 }
308 }
309
310 public void paintBorder(SynthContext context, Graphics g, int x,
311 int y, int w, int h) {
312 context.getPainter().paintInternalFrameTitlePaneBorder(context,
313 g, x, y, w, h);
314 }
315
316 protected LayoutManager createLayout() {
317 SynthContext context = getContext(this );
318 LayoutManager lm = (LayoutManager) style.get(context,
319 "InternalFrameTitlePane.titlePaneLayout");
320 context.dispose();
321 return (lm != null) ? lm : new SynthTitlePaneLayout();
322 }
323
324 public void propertyChange(PropertyChangeEvent evt) {
325 if (evt.getSource() == this ) {
326 if (SynthLookAndFeel.shouldUpdateStyle(evt)) {
327 updateStyle(this );
328 }
329 } else {
330 // Changes for the internal frame
331 if (evt.getPropertyName() == JInternalFrame.FRAME_ICON_PROPERTY) {
332 updateMenuIcon();
333 }
334 }
335 }
336
337 /**
338 * Resets the menuButton icon to match that of the frame.
339 */
340 private void updateMenuIcon() {
341 Icon frameIcon = frame.getFrameIcon();
342 SynthContext context = getContext(this );
343 if (frameIcon != null) {
344 Dimension maxSize = (Dimension) context.getStyle().get(
345 context, "InternalFrameTitlePane.maxFrameIconSize");
346 int maxWidth = 16;
347 int maxHeight = 16;
348 if (maxSize != null) {
349 maxWidth = maxSize.width;
350 maxHeight = maxSize.height;
351 }
352 if ((frameIcon.getIconWidth() > maxWidth || frameIcon
353 .getIconHeight() > maxHeight)
354 && (frameIcon instanceof ImageIcon)) {
355 frameIcon = new ImageIcon(((ImageIcon) frameIcon)
356 .getImage().getScaledInstance(maxWidth,
357 maxHeight, Image.SCALE_SMOOTH));
358 }
359 }
360 context.dispose();
361 menuButton.setIcon(frameIcon);
362 }
363
364 class SynthTitlePaneLayout implements LayoutManager {
365 public void addLayoutComponent(String name, Component c) {
366 }
367
368 public void removeLayoutComponent(Component c) {
369 }
370
371 public Dimension preferredLayoutSize(Container c) {
372 return minimumLayoutSize(c);
373 }
374
375 public Dimension minimumLayoutSize(Container c) {
376 SynthContext context = getContext(SynthInternalFrameTitlePane.this );
377 int width = 0;
378 int height = 0;
379
380 int buttonCount = 0;
381 Dimension pref;
382
383 if (frame.isClosable()) {
384 pref = closeButton.getPreferredSize();
385 width += pref.width;
386 height = Math.max(pref.height, height);
387 buttonCount++;
388 }
389 if (frame.isMaximizable()) {
390 pref = maxButton.getPreferredSize();
391 width += pref.width;
392 height = Math.max(pref.height, height);
393 buttonCount++;
394 }
395 if (frame.isIconifiable()) {
396 pref = iconButton.getPreferredSize();
397 width += pref.width;
398 height = Math.max(pref.height, height);
399 buttonCount++;
400 }
401 pref = menuButton.getPreferredSize();
402 width += pref.width;
403 height = Math.max(pref.height, height);
404
405 width += Math.max(0, (buttonCount - 1) * buttonSpacing);
406
407 FontMetrics fm = SynthInternalFrameTitlePane.this
408 .getFontMetrics(getFont());
409 SynthGraphicsUtils graphicsUtils = context.getStyle()
410 .getGraphicsUtils(context);
411 String frameTitle = frame.getTitle();
412 int title_w = frameTitle != null ? graphicsUtils
413 .computeStringWidth(context, fm.getFont(), fm,
414 frameTitle) : 0;
415 int title_length = frameTitle != null ? frameTitle.length()
416 : 0;
417
418 // Leave room for three characters in the title.
419 if (title_length > 3) {
420 int subtitle_w = graphicsUtils.computeStringWidth(
421 context, fm.getFont(), fm, frameTitle
422 .substring(0, 3)
423 + "...");
424 width += (title_w < subtitle_w) ? title_w : subtitle_w;
425 } else {
426 width += title_w;
427 }
428
429 height = Math.max(fm.getHeight() + 2, height);
430
431 width += titleSpacing + titleSpacing;
432
433 Insets insets = getInsets();
434 height += insets.top + insets.bottom;
435 width += insets.left + insets.right;
436 context.dispose();
437 return new Dimension(width, height);
438 }
439
440 private int center(Component c, Insets insets, int x,
441 boolean trailing) {
442 Dimension pref = c.getPreferredSize();
443 if (trailing) {
444 x -= pref.width;
445 }
446 c
447 .setBounds(
448 x,
449 insets.top
450 + (getHeight() - insets.top
451 - insets.bottom - pref.height)
452 / 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 } else {
479 center(menuButton, insets, getWidth() - insets.right,
480 true);
481 int x = insets.left;
482 if (frame.isClosable()) {
483 x = center(closeButton, insets, x, false);
484 }
485 if (frame.isMaximizable()) {
486 x = center(maxButton, insets, x, false);
487 }
488 if (frame.isIconifiable()) {
489 x = center(iconButton, insets, x, false);
490 }
491 }
492 }
493 }
494
495 private JButton createNoFocusButton() {
496 JButton button = new JButton();
497 button.setFocusable(false);
498 button.setMargin(new Insets(0, 0, 0, 0));
499 return button;
500 }
501 }
|