blob: 19d218871bc0bee9e7ce8ff84ca252aec1d9d6c8 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.swing.plaf.synth;
27
28import java.awt.*;
29import java.awt.event.*;
30import javax.swing.*;
31import javax.swing.plaf.*;
32import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
33import javax.swing.border.*;
34import javax.swing.event.InternalFrameEvent;
35import java.util.EventListener;
36import java.beans.PropertyChangeListener;
37import java.beans.PropertyChangeEvent;
38import java.beans.VetoableChangeListener;
39import java.beans.PropertyVetoException;
40import sun.swing.plaf.synth.SynthUI;
41import 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 */
50class 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 // PENDING: this should all be localizable!
200 JMenuItem mi = (JMenuItem)menu.add(restoreAction);
201 mi.setMnemonic('R');
202 mi = (JMenuItem)menu.add(moveAction);
203 mi.setMnemonic('M');
204 mi = (JMenuItem)menu.add(sizeAction);
205 mi.setMnemonic('S');
206 mi = (JMenuItem)menu.add(iconifyAction);
207 mi.setMnemonic('n');
208 mi = (JMenuItem)menu.add(maximizeAction);
209 mi.setMnemonic('x');
210 menu.add(new JSeparator());
211 mi = (JMenuItem)menu.add(closeAction);
212 mi.setMnemonic('C');
213 }
214
215 protected void showSystemMenu() {
216 Insets insets = frame.getInsets();
217 if (!frame.isIcon()) {
218 systemPopupMenu.show(frame, insets.left, getY() + getHeight());
219 } else {
220 systemPopupMenu.show(menuButton,
221 getX() - insets.left - insets.right,
222 getY() - systemPopupMenu.getPreferredSize().height -
223 insets.bottom - insets.top);
224 }
225 }
226
227 // SynthInternalFrameTitlePane has no UI, we'll invoke paint on it.
228 public void paintComponent(Graphics g) {
229 SynthContext context = getContext(this);
230 SynthLookAndFeel.update(context, g);
231 context.getPainter().paintInternalFrameTitlePaneBackground(context,
232 g, 0, 0, getWidth(), getHeight());
233 paint(context, g);
234 context.dispose();
235 }
236
237 protected void paint(SynthContext context, Graphics g) {
238 String title = frame.getTitle();
239
240 if (title != null) {
241 SynthStyle style = context.getStyle();
242
243 g.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND));
244 g.setFont(style.getFont(context));
245
246 // Center text vertically.
247 FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g);
248 int baseline = (getHeight() + fm.getAscent() - fm.getLeading() -
249 fm.getDescent()) / 2;
250 JButton lastButton = null;
251 if (frame.isIconifiable()) {
252 lastButton = iconButton;
253 }
254 else if (frame.isMaximizable()) {
255 lastButton = maxButton;
256 }
257 else if (frame.isClosable()) {
258 lastButton = closeButton;
259 }
260 int maxX;
261 int minX;
262 boolean ltr = SynthLookAndFeel.isLeftToRight(frame);
263 int titleAlignment = this.titleAlignment;
264 if (ltr) {
265 if (lastButton != null) {
266 maxX = lastButton.getX() - titleSpacing;
267 }
268 else {
269 maxX = frame.getWidth() - frame.getInsets().right -
270 titleSpacing;
271 }
272 minX = menuButton.getX() + menuButton.getWidth() +
273 titleSpacing;
274 }
275 else {
276 if (lastButton != null) {
277 minX = lastButton.getX() + lastButton.getWidth() +
278 titleSpacing;
279 }
280 else {
281 minX = frame.getInsets().left + titleSpacing;
282 }
283 maxX = menuButton.getX() - titleSpacing;
284 if (titleAlignment == SwingConstants.LEADING) {
285 titleAlignment = SwingConstants.TRAILING;
286 }
287 else if (titleAlignment == SwingConstants.TRAILING) {
288 titleAlignment = SwingConstants.LEADING;
289 }
290 }
291 String clippedTitle = getTitle(title, fm, maxX - minX);
292 if (clippedTitle == title) {
293 // String fit, align as necessary.
294 if (titleAlignment == SwingConstants.TRAILING) {
295 minX = maxX - style.getGraphicsUtils(context).
296 computeStringWidth(context, g.getFont(), fm, title);
297 }
298 else if (titleAlignment == SwingConstants.CENTER) {
299 int width = style.getGraphicsUtils(context).
300 computeStringWidth(context, g.getFont(), fm, title);
301 minX = Math.max(minX, (getWidth() - width) / 2);
302 minX = Math.min(maxX - width, minX);
303 }
304 }
305 style.getGraphicsUtils(context).paintText(
306 context, g, 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 =
319 (LayoutManager)style.get(context, "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 }
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 */
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 context.dispose();
362 menuButton.setIcon(frameIcon);
363 }
364
365
366 class SynthTitlePaneLayout implements LayoutManager {
367 public void addLayoutComponent(String name, Component c) {}
368 public void removeLayoutComponent(Component c) {}
369 public Dimension preferredLayoutSize(Container c) {
370 return minimumLayoutSize(c);
371 }
372
373 public Dimension minimumLayoutSize(Container c) {
374 SynthContext context = getContext(
375 SynthInternalFrameTitlePane.this);
376 int width = 0;
377 int height = 0;
378
379 int buttonCount = 0;
380 Dimension pref;
381
382 if (frame.isClosable()) {
383 pref = closeButton.getPreferredSize();
384 width += pref.width;
385 height = Math.max(pref.height, height);
386 buttonCount++;
387 }
388 if (frame.isMaximizable()) {
389 pref = maxButton.getPreferredSize();
390 width += pref.width;
391 height = Math.max(pref.height, height);
392 buttonCount++;
393 }
394 if (frame.isIconifiable()) {
395 pref = iconButton.getPreferredSize();
396 width += pref.width;
397 height = Math.max(pref.height, height);
398 buttonCount++;
399 }
400 pref = menuButton.getPreferredSize();
401 width += pref.width;
402 height = Math.max(pref.height, height);
403
404 width += Math.max(0, (buttonCount - 1) * buttonSpacing);
405
406 FontMetrics fm = SynthInternalFrameTitlePane.this.getFontMetrics(
407 getFont());
408 SynthGraphicsUtils graphicsUtils = context.getStyle().
409 getGraphicsUtils(context);
410 String frameTitle = frame.getTitle();
411 int title_w = frameTitle != null ? graphicsUtils.
412 computeStringWidth(context, fm.getFont(),
413 fm, frameTitle) : 0;
414 int title_length = frameTitle != null ? frameTitle.length() : 0;
415
416 // Leave room for three characters in the title.
417 if (title_length > 3) {
418 int subtitle_w = graphicsUtils.computeStringWidth(context,
419 fm.getFont(), fm, frameTitle.substring(0, 3) + "...");
420 width += (title_w < subtitle_w) ? title_w : subtitle_w;
421 } else {
422 width += title_w;
423 }
424
425 height = Math.max(fm.getHeight() + 2, height);
426
427 width += titleSpacing + titleSpacing;
428
429 Insets insets = getInsets();
430 height += insets.top + insets.bottom;
431 width += insets.left + insets.right;
432 context.dispose();
433 return new Dimension(width, height);
434 }
435
436 private int center(Component c, Insets insets, int x,
437 boolean trailing) {
438 Dimension pref = c.getPreferredSize();
439 if (trailing) {
440 x -= pref.width;
441 }
442 c.setBounds(x, insets.top +
443 (getHeight() - insets.top - insets.bottom -
444 pref.height) / 2, pref.width, pref.height);
445 if (pref.width > 0) {
446 if (trailing) {
447 return x - buttonSpacing;
448 }
449 return x + pref.width + buttonSpacing;
450 }
451 return x;
452 }
453
454 public void layoutContainer(Container c) {
455 Insets insets = c.getInsets();
456 Dimension pref;
457
458 if (SynthLookAndFeel.isLeftToRight(frame)) {
459 center(menuButton, insets, insets.left, false);
460 int x = getWidth() - insets.right;
461 if (frame.isClosable()) {
462 x = center(closeButton, insets, x, true);
463 }
464 if (frame.isMaximizable()) {
465 x = center(maxButton, insets, x, true);
466 }
467 if (frame.isIconifiable()) {
468 x = center(iconButton, insets, x, true);
469 }
470 }
471 else {
472 center(menuButton, insets, getWidth() - insets.right,
473 true);
474 int x = insets.left;
475 if (frame.isClosable()) {
476 x = center(closeButton, insets, x, false);
477 }
478 if (frame.isMaximizable()) {
479 x = center(maxButton, insets, x, false);
480 }
481 if (frame.isIconifiable()) {
482 x = center(iconButton, insets, x, false);
483 }
484 }
485 }
486 }
487
488 private JButton createNoFocusButton() {
489 JButton button = new JButton();
490 button.setFocusable(false);
491 button.setMargin(new Insets(0,0,0,0));
492 return button;
493 }
494}