blob: a0b8ada118f4bd1da2de06340562d245db9233a8 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-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.metal;
27
28import sun.swing.SwingUtilities2;
29import java.awt.*;
30import java.awt.event.*;
31import javax.swing.*;
32import javax.swing.border.*;
33import javax.swing.event.InternalFrameEvent;
34import java.util.EventListener;
35import java.beans.PropertyChangeListener;
36import java.beans.PropertyChangeEvent;
37import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
38
39
40/**
41 * Class that manages a JLF title bar
42 * @author Steve Wilson
43 * @author Brian Beck
44 * @since 1.3
45 */
46
47public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane {
48
49 protected boolean isPalette = false;
50 protected Icon paletteCloseIcon;
51 protected int paletteTitleHeight;
52
53 private static final Border handyEmptyBorder = new EmptyBorder(0,0,0,0);
54
55 /**
56 * Key used to lookup Color from UIManager. If this is null,
57 * <code>getWindowTitleBackground</code> is used.
58 */
59 private String selectedBackgroundKey;
60 /**
61 * Key used to lookup Color from UIManager. If this is null,
62 * <code>getWindowTitleForeground</code> is used.
63 */
64 private String selectedForegroundKey;
65 /**
66 * Key used to lookup shadow color from UIManager. If this is null,
67 * <code>getPrimaryControlDarkShadow</code> is used.
68 */
69 private String selectedShadowKey;
70 /**
71 * Boolean indicating the state of the <code>JInternalFrame</code>s
72 * closable property at <code>updateUI</code> time.
73 */
74 private boolean wasClosable;
75
76 int buttonsWidth = 0;
77
78 MetalBumps activeBumps
79 = new MetalBumps( 0, 0,
80 MetalLookAndFeel.getPrimaryControlHighlight(),
81 MetalLookAndFeel.getPrimaryControlDarkShadow(),
82 (UIManager.get("InternalFrame.activeTitleGradient") != null) ? null :
83 MetalLookAndFeel.getPrimaryControl() );
84 MetalBumps inactiveBumps
85 = new MetalBumps( 0, 0,
86 MetalLookAndFeel.getControlHighlight(),
87 MetalLookAndFeel.getControlDarkShadow(),
88 (UIManager.get("InternalFrame.inactiveTitleGradient") != null) ? null :
89 MetalLookAndFeel.getControl() );
90 MetalBumps paletteBumps;
91
92 private Color activeBumpsHighlight = MetalLookAndFeel.
93 getPrimaryControlHighlight();
94 private Color activeBumpsShadow = MetalLookAndFeel.
95 getPrimaryControlDarkShadow();
96
97 public MetalInternalFrameTitlePane(JInternalFrame f) {
98 super( f );
99 }
100
101 public void addNotify() {
102 super.addNotify();
103 // This is done here instead of in installDefaults as I was worried
104 // that the BasicInternalFrameUI might not be fully initialized, and
105 // that if this resets the closable state the BasicInternalFrameUI
106 // Listeners that get notified might be in an odd/uninitialized state.
107 updateOptionPaneState();
108 }
109
110 protected void installDefaults() {
111 super.installDefaults();
112 setFont( UIManager.getFont("InternalFrame.titleFont") );
113 paletteTitleHeight
114 = UIManager.getInt("InternalFrame.paletteTitleHeight");
115 paletteCloseIcon = UIManager.getIcon("InternalFrame.paletteCloseIcon");
116 wasClosable = frame.isClosable();
117 selectedForegroundKey = selectedBackgroundKey = null;
118 if (MetalLookAndFeel.usingOcean()) {
119 setOpaque(true);
120 }
121 }
122
123 protected void uninstallDefaults() {
124 super.uninstallDefaults();
125 if (wasClosable != frame.isClosable()) {
126 frame.setClosable(wasClosable);
127 }
128 }
129
130 protected void createButtons() {
131 super.createButtons();
132
133 Boolean paintActive = frame.isSelected() ? Boolean.TRUE:Boolean.FALSE;
134 iconButton.putClientProperty("paintActive", paintActive);
135 iconButton.setBorder(handyEmptyBorder);
136
137 maxButton.putClientProperty("paintActive", paintActive);
138 maxButton.setBorder(handyEmptyBorder);
139
140 closeButton.putClientProperty("paintActive", paintActive);
141 closeButton.setBorder(handyEmptyBorder);
142
143 // The palette close icon isn't opaque while the regular close icon is.
144 // This makes sure palette close buttons have the right background.
145 closeButton.setBackground(MetalLookAndFeel.getPrimaryControlShadow());
146
147 if (MetalLookAndFeel.usingOcean()) {
148 iconButton.setContentAreaFilled(false);
149 maxButton.setContentAreaFilled(false);
150 closeButton.setContentAreaFilled(false);
151 }
152 }
153
154 /**
155 * Override the parent's method to do nothing. Metal frames do not
156 * have system menus.
157 */
158 protected void assembleSystemMenu() {}
159
160 /**
161 * Override the parent's method to do nothing. Metal frames do not
162 * have system menus.
163 */
164 protected void addSystemMenuItems(JMenu systemMenu) {}
165
166 /**
167 * Override the parent's method to do nothing. Metal frames do not
168 * have system menus.
169 */
170 protected void showSystemMenu() {}
171
172 /**
173 * Override the parent's method avoid creating a menu bar. Metal frames
174 * do not have system menus.
175 */
176 protected void addSubComponents() {
177 add(iconButton);
178 add(maxButton);
179 add(closeButton);
180 }
181
182 protected PropertyChangeListener createPropertyChangeListener() {
183 return new MetalPropertyChangeHandler();
184 }
185
186 protected LayoutManager createLayout() {
187 return new MetalTitlePaneLayout();
188 }
189
190 class MetalPropertyChangeHandler
191 extends BasicInternalFrameTitlePane.PropertyChangeHandler
192 {
193 public void propertyChange(PropertyChangeEvent evt) {
194 String prop = (String)evt.getPropertyName();
195 if( prop.equals(JInternalFrame.IS_SELECTED_PROPERTY) ) {
196 Boolean b = (Boolean)evt.getNewValue();
197 iconButton.putClientProperty("paintActive", b);
198 closeButton.putClientProperty("paintActive", b);
199 maxButton.putClientProperty("paintActive", b);
200 }
201 else if ("JInternalFrame.messageType".equals(prop)) {
202 updateOptionPaneState();
203 frame.repaint();
204 }
205 super.propertyChange(evt);
206 }
207 }
208
209 class MetalTitlePaneLayout extends TitlePaneLayout {
210 public void addLayoutComponent(String name, Component c) {}
211 public void removeLayoutComponent(Component c) {}
212 public Dimension preferredLayoutSize(Container c) {
213 return minimumLayoutSize(c);
214 }
215
216 public Dimension minimumLayoutSize(Container c) {
217 // Compute width.
218 int width = 30;
219 if (frame.isClosable()) {
220 width += 21;
221 }
222 if (frame.isMaximizable()) {
223 width += 16 + (frame.isClosable() ? 10 : 4);
224 }
225 if (frame.isIconifiable()) {
226 width += 16 + (frame.isMaximizable() ? 2 :
227 (frame.isClosable() ? 10 : 4));
228 }
229 FontMetrics fm = frame.getFontMetrics(getFont());
230 String frameTitle = frame.getTitle();
231 int title_w = frameTitle != null ? SwingUtilities2.stringWidth(
232 frame, fm, frameTitle) : 0;
233 int title_length = frameTitle != null ? frameTitle.length() : 0;
234
235 if (title_length > 2) {
236 int subtitle_w = SwingUtilities2.stringWidth(frame, fm,
237 frame.getTitle().substring(0, 2) + "...");
238 width += (title_w < subtitle_w) ? title_w : subtitle_w;
239 }
240 else {
241 width += title_w;
242 }
243
244 // Compute height.
245 int height = 0;
246 if (isPalette) {
247 height = paletteTitleHeight;
248 } else {
249 int fontHeight = fm.getHeight();
250 fontHeight += 7;
251 Icon icon = frame.getFrameIcon();
252 int iconHeight = 0;
253 if (icon != null) {
254 // SystemMenuBar forces the icon to be 16x16 or less.
255 iconHeight = Math.min(icon.getIconHeight(), 16);
256 }
257 iconHeight += 5;
258 height = Math.max(fontHeight, iconHeight);
259 }
260
261 return new Dimension(width, height);
262 }
263
264 public void layoutContainer(Container c) {
265 boolean leftToRight = MetalUtils.isLeftToRight(frame);
266
267 int w = getWidth();
268 int x = leftToRight ? w : 0;
269 int y = 2;
270 int spacing;
271
272 // assumes all buttons have the same dimensions
273 // these dimensions include the borders
274 int buttonHeight = closeButton.getIcon().getIconHeight();
275 int buttonWidth = closeButton.getIcon().getIconWidth();
276
277 if(frame.isClosable()) {
278 if (isPalette) {
279 spacing = 3;
280 x += leftToRight ? -spacing -(buttonWidth+2) : spacing;
281 closeButton.setBounds(x, y, buttonWidth+2, getHeight()-4);
282 if( !leftToRight ) x += (buttonWidth+2);
283 } else {
284 spacing = 4;
285 x += leftToRight ? -spacing -buttonWidth : spacing;
286 closeButton.setBounds(x, y, buttonWidth, buttonHeight);
287 if( !leftToRight ) x += buttonWidth;
288 }
289 }
290
291 if(frame.isMaximizable() && !isPalette ) {
292 spacing = frame.isClosable() ? 10 : 4;
293 x += leftToRight ? -spacing -buttonWidth : spacing;
294 maxButton.setBounds(x, y, buttonWidth, buttonHeight);
295 if( !leftToRight ) x += buttonWidth;
296 }
297
298 if(frame.isIconifiable() && !isPalette ) {
299 spacing = frame.isMaximizable() ? 2
300 : (frame.isClosable() ? 10 : 4);
301 x += leftToRight ? -spacing -buttonWidth : spacing;
302 iconButton.setBounds(x, y, buttonWidth, buttonHeight);
303 if( !leftToRight ) x += buttonWidth;
304 }
305
306 buttonsWidth = leftToRight ? w - x : x;
307 }
308 }
309
310 public void paintPalette(Graphics g) {
311 boolean leftToRight = MetalUtils.isLeftToRight(frame);
312
313 int width = getWidth();
314 int height = getHeight();
315
316 if (paletteBumps == null) {
317 paletteBumps
318 = new MetalBumps(0, 0,
319 MetalLookAndFeel.getPrimaryControlHighlight(),
320 MetalLookAndFeel.getPrimaryControlInfo(),
321 MetalLookAndFeel.getPrimaryControlShadow() );
322 }
323
324 Color background = MetalLookAndFeel.getPrimaryControlShadow();
325 Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
326
327 g.setColor(background);
328 g.fillRect(0, 0, width, height);
329
330 g.setColor( darkShadow );
331 g.drawLine ( 0, height - 1, width, height -1);
332
333 int xOffset = leftToRight ? 4 : buttonsWidth + 4;
334 int bumpLength = width - buttonsWidth -2*4;
335 int bumpHeight = getHeight() - 4;
336 paletteBumps.setBumpArea( bumpLength, bumpHeight );
337 paletteBumps.paintIcon( this, g, xOffset, 2);
338 }
339
340 public void paintComponent(Graphics g) {
341 if(isPalette) {
342 paintPalette(g);
343 return;
344 }
345
346 boolean leftToRight = MetalUtils.isLeftToRight(frame);
347 boolean isSelected = frame.isSelected();
348
349 int width = getWidth();
350 int height = getHeight();
351
352 Color background = null;
353 Color foreground = null;
354 Color shadow = null;
355
356 MetalBumps bumps;
357 String gradientKey;
358
359 if (isSelected) {
360 if (!MetalLookAndFeel.usingOcean()) {
361 closeButton.setContentAreaFilled(true);
362 maxButton.setContentAreaFilled(true);
363 iconButton.setContentAreaFilled(true);
364 }
365 if (selectedBackgroundKey != null) {
366 background = UIManager.getColor(selectedBackgroundKey);
367 }
368 if (background == null) {
369 background = MetalLookAndFeel.getWindowTitleBackground();
370 }
371 if (selectedForegroundKey != null) {
372 foreground = UIManager.getColor(selectedForegroundKey);
373 }
374 if (selectedShadowKey != null) {
375 shadow = UIManager.getColor(selectedShadowKey);
376 }
377 if (shadow == null) {
378 shadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
379 }
380 if (foreground == null) {
381 foreground = MetalLookAndFeel.getWindowTitleForeground();
382 }
383 activeBumps.setBumpColors(activeBumpsHighlight, activeBumpsShadow,
384 UIManager.get("InternalFrame.activeTitleGradient") !=
385 null ? null : background);
386 bumps = activeBumps;
387 gradientKey = "InternalFrame.activeTitleGradient";
388 } else {
389 if (!MetalLookAndFeel.usingOcean()) {
390 closeButton.setContentAreaFilled(false);
391 maxButton.setContentAreaFilled(false);
392 iconButton.setContentAreaFilled(false);
393 }
394 background = MetalLookAndFeel.getWindowTitleInactiveBackground();
395 foreground = MetalLookAndFeel.getWindowTitleInactiveForeground();
396 shadow = MetalLookAndFeel.getControlDarkShadow();
397 bumps = inactiveBumps;
398 gradientKey = "InternalFrame.inactiveTitleGradient";
399 }
400
401 if (!MetalUtils.drawGradient(this, g, gradientKey, 0, 0, width,
402 height, true)) {
403 g.setColor(background);
404 g.fillRect(0, 0, width, height);
405 }
406
407 g.setColor( shadow );
408 g.drawLine ( 0, height - 1, width, height -1);
409 g.drawLine ( 0, 0, 0 ,0);
410 g.drawLine ( width - 1, 0 , width -1, 0);
411
412
413 int titleLength = 0;
414 int xOffset = leftToRight ? 5 : width - 5;
415 String frameTitle = frame.getTitle();
416
417 Icon icon = frame.getFrameIcon();
418 if ( icon != null ) {
419 if( !leftToRight )
420 xOffset -= icon.getIconWidth();
421 int iconY = ((height / 2) - (icon.getIconHeight() /2));
422 icon.paintIcon(frame, g, xOffset, iconY);
423 xOffset += leftToRight ? icon.getIconWidth() + 5 : -5;
424 }
425
426 if(frameTitle != null) {
427 Font f = getFont();
428 g.setFont(f);
429 FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g, f);
430 int fHeight = fm.getHeight();
431
432 g.setColor(foreground);
433
434 int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent();
435
436 Rectangle rect = new Rectangle(0, 0, 0, 0);
437 if (frame.isIconifiable()) { rect = iconButton.getBounds(); }
438 else if (frame.isMaximizable()) { rect = maxButton.getBounds(); }
439 else if (frame.isClosable()) { rect = closeButton.getBounds(); }
440 int titleW;
441
442 if( leftToRight ) {
443 if (rect.x == 0) {
444 rect.x = frame.getWidth()-frame.getInsets().right-2;
445 }
446 titleW = rect.x - xOffset - 4;
447 frameTitle = getTitle(frameTitle, fm, titleW);
448 } else {
449 titleW = xOffset - rect.x - rect.width - 4;
450 frameTitle = getTitle(frameTitle, fm, titleW);
451 xOffset -= SwingUtilities2.stringWidth(frame, fm, frameTitle);
452 }
453
454 titleLength = SwingUtilities2.stringWidth(frame, fm, frameTitle);
455 SwingUtilities2.drawString(frame, g, frameTitle, xOffset, yOffset);
456 xOffset += leftToRight ? titleLength + 5 : -5;
457 }
458
459 int bumpXOffset;
460 int bumpLength;
461 if( leftToRight ) {
462 bumpLength = width - buttonsWidth - xOffset - 5;
463 bumpXOffset = xOffset;
464 } else {
465 bumpLength = xOffset - buttonsWidth - 5;
466 bumpXOffset = buttonsWidth + 5;
467 }
468 int bumpYOffset = 3;
469 int bumpHeight = getHeight() - (2 * bumpYOffset);
470 bumps.setBumpArea( bumpLength, bumpHeight );
471 bumps.paintIcon(this, g, bumpXOffset, bumpYOffset);
472 }
473
474 public void setPalette(boolean b) {
475 isPalette = b;
476
477 if (isPalette) {
478 closeButton.setIcon(paletteCloseIcon);
479 if( frame.isMaximizable() )
480 remove(maxButton);
481 if( frame.isIconifiable() )
482 remove(iconButton);
483 } else {
484 closeButton.setIcon(closeIcon);
485 if( frame.isMaximizable() )
486 add(maxButton);
487 if( frame.isIconifiable() )
488 add(iconButton);
489 }
490 revalidate();
491 repaint();
492 }
493
494 /**
495 * Updates any state dependant upon the JInternalFrame being shown in
496 * a <code>JOptionPane</code>.
497 */
498 private void updateOptionPaneState() {
499 int type = -2;
500 boolean closable = wasClosable;
501 Object obj = frame.getClientProperty("JInternalFrame.messageType");
502
503 if (obj == null) {
504 // Don't change the closable state unless in an JOptionPane.
505 return;
506 }
507 if (obj instanceof Integer) {
508 type = ((Integer) obj).intValue();
509 }
510 switch (type) {
511 case JOptionPane.ERROR_MESSAGE:
512 selectedBackgroundKey =
513 "OptionPane.errorDialog.titlePane.background";
514 selectedForegroundKey =
515 "OptionPane.errorDialog.titlePane.foreground";
516 selectedShadowKey = "OptionPane.errorDialog.titlePane.shadow";
517 closable = false;
518 break;
519 case JOptionPane.QUESTION_MESSAGE:
520 selectedBackgroundKey =
521 "OptionPane.questionDialog.titlePane.background";
522 selectedForegroundKey =
523 "OptionPane.questionDialog.titlePane.foreground";
524 selectedShadowKey =
525 "OptionPane.questionDialog.titlePane.shadow";
526 closable = false;
527 break;
528 case JOptionPane.WARNING_MESSAGE:
529 selectedBackgroundKey =
530 "OptionPane.warningDialog.titlePane.background";
531 selectedForegroundKey =
532 "OptionPane.warningDialog.titlePane.foreground";
533 selectedShadowKey = "OptionPane.warningDialog.titlePane.shadow";
534 closable = false;
535 break;
536 case JOptionPane.INFORMATION_MESSAGE:
537 case JOptionPane.PLAIN_MESSAGE:
538 selectedBackgroundKey = selectedForegroundKey = selectedShadowKey =
539 null;
540 closable = false;
541 break;
542 default:
543 selectedBackgroundKey = selectedForegroundKey = selectedShadowKey =
544 null;
545 break;
546 }
547 if (closable != frame.isClosable()) {
548 frame.setClosable(closable);
549 }
550 }
551}