|
1 /* |
|
2 * Copyright (c) 2000, 2014, 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.metal; |
|
27 |
|
28 import sun.swing.SwingUtilities2; |
|
29 import sun.awt.SunToolkit; |
|
30 import java.awt.*; |
|
31 import java.awt.event.*; |
|
32 import java.beans.*; |
|
33 import javax.swing.*; |
|
34 import javax.swing.border.*; |
|
35 import javax.swing.event.InternalFrameEvent; |
|
36 import javax.swing.plaf.*; |
|
37 import javax.swing.plaf.basic.*; |
|
38 import java.util.Locale; |
|
39 import javax.accessibility.*; |
|
40 |
|
41 |
|
42 /** |
|
43 * Class that manages a JLF awt.Window-descendant class's title bar. |
|
44 * <p> |
|
45 * This class assumes it will be created with a particular window |
|
46 * decoration style, and that if the style changes, a new one will |
|
47 * be created. |
|
48 * |
|
49 * @author Terry Kellerman |
|
50 * @since 1.4 |
|
51 */ |
|
52 @SuppressWarnings("serial") // Superclass is not serializable across versions |
|
53 class MetalTitlePane extends JComponent { |
|
54 private static final Border handyEmptyBorder = new EmptyBorder(0,0,0,0); |
|
55 private static final int IMAGE_HEIGHT = 16; |
|
56 private static final int IMAGE_WIDTH = 16; |
|
57 |
|
58 /** |
|
59 * PropertyChangeListener added to the JRootPane. |
|
60 */ |
|
61 private PropertyChangeListener propertyChangeListener; |
|
62 |
|
63 /** |
|
64 * JMenuBar, typically renders the system menu items. |
|
65 */ |
|
66 private JMenuBar menuBar; |
|
67 /** |
|
68 * Action used to close the Window. |
|
69 */ |
|
70 private Action closeAction; |
|
71 |
|
72 /** |
|
73 * Action used to iconify the Frame. |
|
74 */ |
|
75 private Action iconifyAction; |
|
76 |
|
77 /** |
|
78 * Action to restore the Frame size. |
|
79 */ |
|
80 private Action restoreAction; |
|
81 |
|
82 /** |
|
83 * Action to restore the Frame size. |
|
84 */ |
|
85 private Action maximizeAction; |
|
86 |
|
87 /** |
|
88 * Button used to maximize or restore the Frame. |
|
89 */ |
|
90 private JButton toggleButton; |
|
91 |
|
92 /** |
|
93 * Button used to maximize or restore the Frame. |
|
94 */ |
|
95 private JButton iconifyButton; |
|
96 |
|
97 /** |
|
98 * Button used to maximize or restore the Frame. |
|
99 */ |
|
100 private JButton closeButton; |
|
101 |
|
102 /** |
|
103 * Icon used for toggleButton when window is normal size. |
|
104 */ |
|
105 private Icon maximizeIcon; |
|
106 |
|
107 /** |
|
108 * Icon used for toggleButton when window is maximized. |
|
109 */ |
|
110 private Icon minimizeIcon; |
|
111 |
|
112 /** |
|
113 * Image used for the system menu icon |
|
114 */ |
|
115 private Image systemIcon; |
|
116 |
|
117 /** |
|
118 * Listens for changes in the state of the Window listener to update |
|
119 * the state of the widgets. |
|
120 */ |
|
121 private WindowListener windowListener; |
|
122 |
|
123 /** |
|
124 * Window we're currently in. |
|
125 */ |
|
126 private Window window; |
|
127 |
|
128 /** |
|
129 * JRootPane rendering for. |
|
130 */ |
|
131 private JRootPane rootPane; |
|
132 |
|
133 /** |
|
134 * Room remaining in title for bumps. |
|
135 */ |
|
136 private int buttonsWidth; |
|
137 |
|
138 /** |
|
139 * Buffered Frame.state property. As state isn't bound, this is kept |
|
140 * to determine when to avoid updating widgets. |
|
141 */ |
|
142 private int state; |
|
143 |
|
144 /** |
|
145 * MetalRootPaneUI that created us. |
|
146 */ |
|
147 private MetalRootPaneUI rootPaneUI; |
|
148 |
|
149 |
|
150 // Colors |
|
151 private Color inactiveBackground = UIManager.getColor("inactiveCaption"); |
|
152 private Color inactiveForeground = UIManager.getColor("inactiveCaptionText"); |
|
153 private Color inactiveShadow = UIManager.getColor("inactiveCaptionBorder"); |
|
154 private Color activeBumpsHighlight = MetalLookAndFeel.getPrimaryControlHighlight(); |
|
155 private Color activeBumpsShadow = MetalLookAndFeel.getPrimaryControlDarkShadow(); |
|
156 private Color activeBackground = null; |
|
157 private Color activeForeground = null; |
|
158 private Color activeShadow = null; |
|
159 |
|
160 // Bumps |
|
161 private MetalBumps activeBumps |
|
162 = new MetalBumps( 0, 0, |
|
163 activeBumpsHighlight, |
|
164 activeBumpsShadow, |
|
165 MetalLookAndFeel.getPrimaryControl() ); |
|
166 private MetalBumps inactiveBumps |
|
167 = new MetalBumps( 0, 0, |
|
168 MetalLookAndFeel.getControlHighlight(), |
|
169 MetalLookAndFeel.getControlDarkShadow(), |
|
170 MetalLookAndFeel.getControl() ); |
|
171 |
|
172 |
|
173 public MetalTitlePane(JRootPane root, MetalRootPaneUI ui) { |
|
174 this.rootPane = root; |
|
175 rootPaneUI = ui; |
|
176 |
|
177 state = -1; |
|
178 |
|
179 installSubcomponents(); |
|
180 determineColors(); |
|
181 installDefaults(); |
|
182 |
|
183 setLayout(createLayout()); |
|
184 } |
|
185 |
|
186 /** |
|
187 * Uninstalls the necessary state. |
|
188 */ |
|
189 private void uninstall() { |
|
190 uninstallListeners(); |
|
191 window = null; |
|
192 removeAll(); |
|
193 } |
|
194 |
|
195 /** |
|
196 * Installs the necessary listeners. |
|
197 */ |
|
198 private void installListeners() { |
|
199 if (window != null) { |
|
200 windowListener = createWindowListener(); |
|
201 window.addWindowListener(windowListener); |
|
202 propertyChangeListener = createWindowPropertyChangeListener(); |
|
203 window.addPropertyChangeListener(propertyChangeListener); |
|
204 } |
|
205 } |
|
206 |
|
207 /** |
|
208 * Uninstalls the necessary listeners. |
|
209 */ |
|
210 private void uninstallListeners() { |
|
211 if (window != null) { |
|
212 window.removeWindowListener(windowListener); |
|
213 window.removePropertyChangeListener(propertyChangeListener); |
|
214 } |
|
215 } |
|
216 |
|
217 /** |
|
218 * Returns the <code>WindowListener</code> to add to the |
|
219 * <code>Window</code>. |
|
220 */ |
|
221 private WindowListener createWindowListener() { |
|
222 return new WindowHandler(); |
|
223 } |
|
224 |
|
225 /** |
|
226 * Returns the <code>PropertyChangeListener</code> to install on |
|
227 * the <code>Window</code>. |
|
228 */ |
|
229 private PropertyChangeListener createWindowPropertyChangeListener() { |
|
230 return new PropertyChangeHandler(); |
|
231 } |
|
232 |
|
233 /** |
|
234 * Returns the <code>JRootPane</code> this was created for. |
|
235 */ |
|
236 public JRootPane getRootPane() { |
|
237 return rootPane; |
|
238 } |
|
239 |
|
240 /** |
|
241 * Returns the decoration style of the <code>JRootPane</code>. |
|
242 */ |
|
243 private int getWindowDecorationStyle() { |
|
244 return getRootPane().getWindowDecorationStyle(); |
|
245 } |
|
246 |
|
247 public void addNotify() { |
|
248 super.addNotify(); |
|
249 |
|
250 uninstallListeners(); |
|
251 |
|
252 window = SwingUtilities.getWindowAncestor(this); |
|
253 if (window != null) { |
|
254 if (window instanceof Frame) { |
|
255 setState(((Frame)window).getExtendedState()); |
|
256 } |
|
257 else { |
|
258 setState(0); |
|
259 } |
|
260 setActive(window.isActive()); |
|
261 installListeners(); |
|
262 updateSystemIcon(); |
|
263 } |
|
264 } |
|
265 |
|
266 public void removeNotify() { |
|
267 super.removeNotify(); |
|
268 |
|
269 uninstallListeners(); |
|
270 window = null; |
|
271 } |
|
272 |
|
273 /** |
|
274 * Adds any sub-Components contained in the <code>MetalTitlePane</code>. |
|
275 */ |
|
276 private void installSubcomponents() { |
|
277 int decorationStyle = getWindowDecorationStyle(); |
|
278 if (decorationStyle == JRootPane.FRAME) { |
|
279 createActions(); |
|
280 menuBar = createMenuBar(); |
|
281 add(menuBar); |
|
282 createButtons(); |
|
283 add(iconifyButton); |
|
284 add(toggleButton); |
|
285 add(closeButton); |
|
286 } else if (decorationStyle == JRootPane.PLAIN_DIALOG || |
|
287 decorationStyle == JRootPane.INFORMATION_DIALOG || |
|
288 decorationStyle == JRootPane.ERROR_DIALOG || |
|
289 decorationStyle == JRootPane.COLOR_CHOOSER_DIALOG || |
|
290 decorationStyle == JRootPane.FILE_CHOOSER_DIALOG || |
|
291 decorationStyle == JRootPane.QUESTION_DIALOG || |
|
292 decorationStyle == JRootPane.WARNING_DIALOG) { |
|
293 createActions(); |
|
294 createButtons(); |
|
295 add(closeButton); |
|
296 } |
|
297 } |
|
298 |
|
299 /** |
|
300 * Determines the Colors to draw with. |
|
301 */ |
|
302 private void determineColors() { |
|
303 switch (getWindowDecorationStyle()) { |
|
304 case JRootPane.FRAME: |
|
305 activeBackground = UIManager.getColor("activeCaption"); |
|
306 activeForeground = UIManager.getColor("activeCaptionText"); |
|
307 activeShadow = UIManager.getColor("activeCaptionBorder"); |
|
308 break; |
|
309 case JRootPane.ERROR_DIALOG: |
|
310 activeBackground = UIManager.getColor( |
|
311 "OptionPane.errorDialog.titlePane.background"); |
|
312 activeForeground = UIManager.getColor( |
|
313 "OptionPane.errorDialog.titlePane.foreground"); |
|
314 activeShadow = UIManager.getColor( |
|
315 "OptionPane.errorDialog.titlePane.shadow"); |
|
316 break; |
|
317 case JRootPane.QUESTION_DIALOG: |
|
318 case JRootPane.COLOR_CHOOSER_DIALOG: |
|
319 case JRootPane.FILE_CHOOSER_DIALOG: |
|
320 activeBackground = UIManager.getColor( |
|
321 "OptionPane.questionDialog.titlePane.background"); |
|
322 activeForeground = UIManager.getColor( |
|
323 "OptionPane.questionDialog.titlePane.foreground"); |
|
324 activeShadow = UIManager.getColor( |
|
325 "OptionPane.questionDialog.titlePane.shadow"); |
|
326 break; |
|
327 case JRootPane.WARNING_DIALOG: |
|
328 activeBackground = UIManager.getColor( |
|
329 "OptionPane.warningDialog.titlePane.background"); |
|
330 activeForeground = UIManager.getColor( |
|
331 "OptionPane.warningDialog.titlePane.foreground"); |
|
332 activeShadow = UIManager.getColor( |
|
333 "OptionPane.warningDialog.titlePane.shadow"); |
|
334 break; |
|
335 case JRootPane.PLAIN_DIALOG: |
|
336 case JRootPane.INFORMATION_DIALOG: |
|
337 default: |
|
338 activeBackground = UIManager.getColor("activeCaption"); |
|
339 activeForeground = UIManager.getColor("activeCaptionText"); |
|
340 activeShadow = UIManager.getColor("activeCaptionBorder"); |
|
341 break; |
|
342 } |
|
343 activeBumps.setBumpColors(activeBumpsHighlight, activeBumpsShadow, |
|
344 activeBackground); |
|
345 } |
|
346 |
|
347 /** |
|
348 * Installs the fonts and necessary properties on the MetalTitlePane. |
|
349 */ |
|
350 private void installDefaults() { |
|
351 setFont(UIManager.getFont("InternalFrame.titleFont", getLocale())); |
|
352 } |
|
353 |
|
354 /** |
|
355 * Uninstalls any previously installed UI values. |
|
356 */ |
|
357 private void uninstallDefaults() { |
|
358 } |
|
359 |
|
360 /** |
|
361 * Returns the <code>JMenuBar</code> displaying the appropriate |
|
362 * system menu items. |
|
363 */ |
|
364 protected JMenuBar createMenuBar() { |
|
365 menuBar = new SystemMenuBar(); |
|
366 menuBar.setFocusable(false); |
|
367 menuBar.setBorderPainted(true); |
|
368 menuBar.add(createMenu()); |
|
369 return menuBar; |
|
370 } |
|
371 |
|
372 /** |
|
373 * Closes the Window. |
|
374 */ |
|
375 private void close() { |
|
376 Window window = getWindow(); |
|
377 |
|
378 if (window != null) { |
|
379 window.dispatchEvent(new WindowEvent( |
|
380 window, WindowEvent.WINDOW_CLOSING)); |
|
381 } |
|
382 } |
|
383 |
|
384 /** |
|
385 * Iconifies the Frame. |
|
386 */ |
|
387 private void iconify() { |
|
388 Frame frame = getFrame(); |
|
389 if (frame != null) { |
|
390 frame.setExtendedState(state | Frame.ICONIFIED); |
|
391 } |
|
392 } |
|
393 |
|
394 /** |
|
395 * Maximizes the Frame. |
|
396 */ |
|
397 private void maximize() { |
|
398 Frame frame = getFrame(); |
|
399 if (frame != null) { |
|
400 frame.setExtendedState(state | Frame.MAXIMIZED_BOTH); |
|
401 } |
|
402 } |
|
403 |
|
404 /** |
|
405 * Restores the Frame size. |
|
406 */ |
|
407 private void restore() { |
|
408 Frame frame = getFrame(); |
|
409 |
|
410 if (frame == null) { |
|
411 return; |
|
412 } |
|
413 |
|
414 if ((state & Frame.ICONIFIED) != 0) { |
|
415 frame.setExtendedState(state & ~Frame.ICONIFIED); |
|
416 } else { |
|
417 frame.setExtendedState(state & ~Frame.MAXIMIZED_BOTH); |
|
418 } |
|
419 } |
|
420 |
|
421 /** |
|
422 * Create the <code>Action</code>s that get associated with the |
|
423 * buttons and menu items. |
|
424 */ |
|
425 private void createActions() { |
|
426 closeAction = new CloseAction(); |
|
427 if (getWindowDecorationStyle() == JRootPane.FRAME) { |
|
428 iconifyAction = new IconifyAction(); |
|
429 restoreAction = new RestoreAction(); |
|
430 maximizeAction = new MaximizeAction(); |
|
431 } |
|
432 } |
|
433 |
|
434 /** |
|
435 * Returns the <code>JMenu</code> displaying the appropriate menu items |
|
436 * for manipulating the Frame. |
|
437 */ |
|
438 private JMenu createMenu() { |
|
439 JMenu menu = new JMenu(""); |
|
440 if (getWindowDecorationStyle() == JRootPane.FRAME) { |
|
441 addMenuItems(menu); |
|
442 } |
|
443 return menu; |
|
444 } |
|
445 |
|
446 /** |
|
447 * Adds the necessary <code>JMenuItem</code>s to the passed in menu. |
|
448 */ |
|
449 private void addMenuItems(JMenu menu) { |
|
450 Locale locale = getRootPane().getLocale(); |
|
451 JMenuItem mi = menu.add(restoreAction); |
|
452 int mnemonic = MetalUtils.getInt("MetalTitlePane.restoreMnemonic", -1); |
|
453 |
|
454 if (mnemonic != -1) { |
|
455 mi.setMnemonic(mnemonic); |
|
456 } |
|
457 |
|
458 mi = menu.add(iconifyAction); |
|
459 mnemonic = MetalUtils.getInt("MetalTitlePane.iconifyMnemonic", -1); |
|
460 if (mnemonic != -1) { |
|
461 mi.setMnemonic(mnemonic); |
|
462 } |
|
463 |
|
464 if (Toolkit.getDefaultToolkit().isFrameStateSupported( |
|
465 Frame.MAXIMIZED_BOTH)) { |
|
466 mi = menu.add(maximizeAction); |
|
467 mnemonic = |
|
468 MetalUtils.getInt("MetalTitlePane.maximizeMnemonic", -1); |
|
469 if (mnemonic != -1) { |
|
470 mi.setMnemonic(mnemonic); |
|
471 } |
|
472 } |
|
473 |
|
474 menu.add(new JSeparator()); |
|
475 |
|
476 mi = menu.add(closeAction); |
|
477 mnemonic = MetalUtils.getInt("MetalTitlePane.closeMnemonic", -1); |
|
478 if (mnemonic != -1) { |
|
479 mi.setMnemonic(mnemonic); |
|
480 } |
|
481 } |
|
482 |
|
483 /** |
|
484 * Returns a <code>JButton</code> appropriate for placement on the |
|
485 * TitlePane. |
|
486 */ |
|
487 private JButton createTitleButton() { |
|
488 JButton button = new JButton(); |
|
489 |
|
490 button.setFocusPainted(false); |
|
491 button.setFocusable(false); |
|
492 button.setOpaque(true); |
|
493 return button; |
|
494 } |
|
495 |
|
496 /** |
|
497 * Creates the Buttons that will be placed on the TitlePane. |
|
498 */ |
|
499 private void createButtons() { |
|
500 closeButton = createTitleButton(); |
|
501 closeButton.setAction(closeAction); |
|
502 closeButton.setText(null); |
|
503 closeButton.putClientProperty("paintActive", Boolean.TRUE); |
|
504 closeButton.setBorder(handyEmptyBorder); |
|
505 closeButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, |
|
506 "Close"); |
|
507 closeButton.setIcon(UIManager.getIcon("InternalFrame.closeIcon")); |
|
508 |
|
509 if (getWindowDecorationStyle() == JRootPane.FRAME) { |
|
510 maximizeIcon = UIManager.getIcon("InternalFrame.maximizeIcon"); |
|
511 minimizeIcon = UIManager.getIcon("InternalFrame.minimizeIcon"); |
|
512 |
|
513 iconifyButton = createTitleButton(); |
|
514 iconifyButton.setAction(iconifyAction); |
|
515 iconifyButton.setText(null); |
|
516 iconifyButton.putClientProperty("paintActive", Boolean.TRUE); |
|
517 iconifyButton.setBorder(handyEmptyBorder); |
|
518 iconifyButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, |
|
519 "Iconify"); |
|
520 iconifyButton.setIcon(UIManager.getIcon("InternalFrame.iconifyIcon")); |
|
521 |
|
522 toggleButton = createTitleButton(); |
|
523 toggleButton.setAction(restoreAction); |
|
524 toggleButton.putClientProperty("paintActive", Boolean.TRUE); |
|
525 toggleButton.setBorder(handyEmptyBorder); |
|
526 toggleButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, |
|
527 "Maximize"); |
|
528 toggleButton.setIcon(maximizeIcon); |
|
529 } |
|
530 } |
|
531 |
|
532 /** |
|
533 * Returns the <code>LayoutManager</code> that should be installed on |
|
534 * the <code>MetalTitlePane</code>. |
|
535 */ |
|
536 private LayoutManager createLayout() { |
|
537 return new TitlePaneLayout(); |
|
538 } |
|
539 |
|
540 /** |
|
541 * Updates state dependant upon the Window's active state. |
|
542 */ |
|
543 private void setActive(boolean isActive) { |
|
544 Boolean activeB = isActive ? Boolean.TRUE : Boolean.FALSE; |
|
545 |
|
546 closeButton.putClientProperty("paintActive", activeB); |
|
547 if (getWindowDecorationStyle() == JRootPane.FRAME) { |
|
548 iconifyButton.putClientProperty("paintActive", activeB); |
|
549 toggleButton.putClientProperty("paintActive", activeB); |
|
550 } |
|
551 // Repaint the whole thing as the Borders that are used have |
|
552 // different colors for active vs inactive |
|
553 getRootPane().repaint(); |
|
554 } |
|
555 |
|
556 /** |
|
557 * Sets the state of the Window. |
|
558 */ |
|
559 private void setState(int state) { |
|
560 setState(state, false); |
|
561 } |
|
562 |
|
563 /** |
|
564 * Sets the state of the window. If <code>updateRegardless</code> is |
|
565 * true and the state has not changed, this will update anyway. |
|
566 */ |
|
567 private void setState(int state, boolean updateRegardless) { |
|
568 Window w = getWindow(); |
|
569 |
|
570 if (w != null && getWindowDecorationStyle() == JRootPane.FRAME) { |
|
571 if (this.state == state && !updateRegardless) { |
|
572 return; |
|
573 } |
|
574 Frame frame = getFrame(); |
|
575 |
|
576 if (frame != null) { |
|
577 JRootPane rootPane = getRootPane(); |
|
578 |
|
579 if (((state & Frame.MAXIMIZED_BOTH) != 0) && |
|
580 (rootPane.getBorder() == null || |
|
581 (rootPane.getBorder() instanceof UIResource)) && |
|
582 frame.isShowing()) { |
|
583 rootPane.setBorder(null); |
|
584 } |
|
585 else if ((state & Frame.MAXIMIZED_BOTH) == 0) { |
|
586 // This is a croak, if state becomes bound, this can |
|
587 // be nuked. |
|
588 rootPaneUI.installBorder(rootPane); |
|
589 } |
|
590 if (frame.isResizable()) { |
|
591 if ((state & Frame.MAXIMIZED_BOTH) != 0) { |
|
592 updateToggleButton(restoreAction, minimizeIcon); |
|
593 maximizeAction.setEnabled(false); |
|
594 restoreAction.setEnabled(true); |
|
595 } |
|
596 else { |
|
597 updateToggleButton(maximizeAction, maximizeIcon); |
|
598 maximizeAction.setEnabled(true); |
|
599 restoreAction.setEnabled(false); |
|
600 } |
|
601 if (toggleButton.getParent() == null || |
|
602 iconifyButton.getParent() == null) { |
|
603 add(toggleButton); |
|
604 add(iconifyButton); |
|
605 revalidate(); |
|
606 repaint(); |
|
607 } |
|
608 toggleButton.setText(null); |
|
609 } |
|
610 else { |
|
611 maximizeAction.setEnabled(false); |
|
612 restoreAction.setEnabled(false); |
|
613 if (toggleButton.getParent() != null) { |
|
614 remove(toggleButton); |
|
615 revalidate(); |
|
616 repaint(); |
|
617 } |
|
618 } |
|
619 } |
|
620 else { |
|
621 // Not contained in a Frame |
|
622 maximizeAction.setEnabled(false); |
|
623 restoreAction.setEnabled(false); |
|
624 iconifyAction.setEnabled(false); |
|
625 remove(toggleButton); |
|
626 remove(iconifyButton); |
|
627 revalidate(); |
|
628 repaint(); |
|
629 } |
|
630 closeAction.setEnabled(true); |
|
631 this.state = state; |
|
632 } |
|
633 } |
|
634 |
|
635 /** |
|
636 * Updates the toggle button to contain the Icon <code>icon</code>, and |
|
637 * Action <code>action</code>. |
|
638 */ |
|
639 private void updateToggleButton(Action action, Icon icon) { |
|
640 toggleButton.setAction(action); |
|
641 toggleButton.setIcon(icon); |
|
642 toggleButton.setText(null); |
|
643 } |
|
644 |
|
645 /** |
|
646 * Returns the Frame rendering in. This will return null if the |
|
647 * <code>JRootPane</code> is not contained in a <code>Frame</code>. |
|
648 */ |
|
649 private Frame getFrame() { |
|
650 Window window = getWindow(); |
|
651 |
|
652 if (window instanceof Frame) { |
|
653 return (Frame)window; |
|
654 } |
|
655 return null; |
|
656 } |
|
657 |
|
658 /** |
|
659 * Returns the <code>Window</code> the <code>JRootPane</code> is |
|
660 * contained in. This will return null if there is no parent ancestor |
|
661 * of the <code>JRootPane</code>. |
|
662 */ |
|
663 private Window getWindow() { |
|
664 return window; |
|
665 } |
|
666 |
|
667 /** |
|
668 * Returns the String to display as the title. |
|
669 */ |
|
670 private String getTitle() { |
|
671 Window w = getWindow(); |
|
672 |
|
673 if (w instanceof Frame) { |
|
674 return ((Frame)w).getTitle(); |
|
675 } |
|
676 else if (w instanceof Dialog) { |
|
677 return ((Dialog)w).getTitle(); |
|
678 } |
|
679 return null; |
|
680 } |
|
681 |
|
682 /** |
|
683 * Renders the TitlePane. |
|
684 */ |
|
685 public void paintComponent(Graphics g) { |
|
686 // As state isn't bound, we need a convenience place to check |
|
687 // if it has changed. Changing the state typically changes the |
|
688 if (getFrame() != null) { |
|
689 setState(getFrame().getExtendedState()); |
|
690 } |
|
691 JRootPane rootPane = getRootPane(); |
|
692 Window window = getWindow(); |
|
693 boolean leftToRight = (window == null) ? |
|
694 rootPane.getComponentOrientation().isLeftToRight() : |
|
695 window.getComponentOrientation().isLeftToRight(); |
|
696 boolean isSelected = (window == null) ? true : window.isActive(); |
|
697 int width = getWidth(); |
|
698 int height = getHeight(); |
|
699 |
|
700 Color background; |
|
701 Color foreground; |
|
702 Color darkShadow; |
|
703 |
|
704 MetalBumps bumps; |
|
705 |
|
706 if (isSelected) { |
|
707 background = activeBackground; |
|
708 foreground = activeForeground; |
|
709 darkShadow = activeShadow; |
|
710 bumps = activeBumps; |
|
711 } else { |
|
712 background = inactiveBackground; |
|
713 foreground = inactiveForeground; |
|
714 darkShadow = inactiveShadow; |
|
715 bumps = inactiveBumps; |
|
716 } |
|
717 |
|
718 g.setColor(background); |
|
719 g.fillRect(0, 0, width, height); |
|
720 |
|
721 g.setColor( darkShadow ); |
|
722 g.drawLine ( 0, height - 1, width, height -1); |
|
723 g.drawLine ( 0, 0, 0 ,0); |
|
724 g.drawLine ( width - 1, 0 , width -1, 0); |
|
725 |
|
726 int xOffset = leftToRight ? 5 : width - 5; |
|
727 |
|
728 if (getWindowDecorationStyle() == JRootPane.FRAME) { |
|
729 xOffset += leftToRight ? IMAGE_WIDTH + 5 : - IMAGE_WIDTH - 5; |
|
730 } |
|
731 |
|
732 String theTitle = getTitle(); |
|
733 if (theTitle != null) { |
|
734 FontMetrics fm = SwingUtilities2.getFontMetrics(rootPane, g); |
|
735 |
|
736 g.setColor(foreground); |
|
737 |
|
738 int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent(); |
|
739 |
|
740 Rectangle rect = new Rectangle(0, 0, 0, 0); |
|
741 if (iconifyButton != null && iconifyButton.getParent() != null) { |
|
742 rect = iconifyButton.getBounds(); |
|
743 } |
|
744 int titleW; |
|
745 |
|
746 if( leftToRight ) { |
|
747 if (rect.x == 0) { |
|
748 rect.x = window.getWidth() - window.getInsets().right-2; |
|
749 } |
|
750 titleW = rect.x - xOffset - 4; |
|
751 theTitle = SwingUtilities2.clipStringIfNecessary( |
|
752 rootPane, fm, theTitle, titleW); |
|
753 } else { |
|
754 titleW = xOffset - rect.x - rect.width - 4; |
|
755 theTitle = SwingUtilities2.clipStringIfNecessary( |
|
756 rootPane, fm, theTitle, titleW); |
|
757 xOffset -= SwingUtilities2.stringWidth(rootPane, fm, |
|
758 theTitle); |
|
759 } |
|
760 int titleLength = SwingUtilities2.stringWidth(rootPane, fm, |
|
761 theTitle); |
|
762 SwingUtilities2.drawString(rootPane, g, theTitle, xOffset, |
|
763 yOffset ); |
|
764 xOffset += leftToRight ? titleLength + 5 : -5; |
|
765 } |
|
766 |
|
767 int bumpXOffset; |
|
768 int bumpLength; |
|
769 if( leftToRight ) { |
|
770 bumpLength = width - buttonsWidth - xOffset - 5; |
|
771 bumpXOffset = xOffset; |
|
772 } else { |
|
773 bumpLength = xOffset - buttonsWidth - 5; |
|
774 bumpXOffset = buttonsWidth + 5; |
|
775 } |
|
776 int bumpYOffset = 3; |
|
777 int bumpHeight = getHeight() - (2 * bumpYOffset); |
|
778 bumps.setBumpArea( bumpLength, bumpHeight ); |
|
779 bumps.paintIcon(this, g, bumpXOffset, bumpYOffset); |
|
780 } |
|
781 |
|
782 /** |
|
783 * Actions used to <code>close</code> the <code>Window</code>. |
|
784 */ |
|
785 @SuppressWarnings("serial") // Superclass is not serializable across versions |
|
786 private class CloseAction extends AbstractAction { |
|
787 public CloseAction() { |
|
788 super(UIManager.getString("MetalTitlePane.closeTitle", |
|
789 getLocale())); |
|
790 } |
|
791 |
|
792 public void actionPerformed(ActionEvent e) { |
|
793 close(); |
|
794 } |
|
795 } |
|
796 |
|
797 |
|
798 /** |
|
799 * Actions used to <code>iconfiy</code> the <code>Frame</code>. |
|
800 */ |
|
801 @SuppressWarnings("serial") // Superclass is not serializable across versions |
|
802 private class IconifyAction extends AbstractAction { |
|
803 public IconifyAction() { |
|
804 super(UIManager.getString("MetalTitlePane.iconifyTitle", |
|
805 getLocale())); |
|
806 } |
|
807 |
|
808 public void actionPerformed(ActionEvent e) { |
|
809 iconify(); |
|
810 } |
|
811 } |
|
812 |
|
813 |
|
814 /** |
|
815 * Actions used to <code>restore</code> the <code>Frame</code>. |
|
816 */ |
|
817 @SuppressWarnings("serial") // Superclass is not serializable across versions |
|
818 private class RestoreAction extends AbstractAction { |
|
819 public RestoreAction() { |
|
820 super(UIManager.getString |
|
821 ("MetalTitlePane.restoreTitle", getLocale())); |
|
822 } |
|
823 |
|
824 public void actionPerformed(ActionEvent e) { |
|
825 restore(); |
|
826 } |
|
827 } |
|
828 |
|
829 |
|
830 /** |
|
831 * Actions used to <code>restore</code> the <code>Frame</code>. |
|
832 */ |
|
833 @SuppressWarnings("serial") // Superclass is not serializable across versions |
|
834 private class MaximizeAction extends AbstractAction { |
|
835 public MaximizeAction() { |
|
836 super(UIManager.getString("MetalTitlePane.maximizeTitle", |
|
837 getLocale())); |
|
838 } |
|
839 |
|
840 public void actionPerformed(ActionEvent e) { |
|
841 maximize(); |
|
842 } |
|
843 } |
|
844 |
|
845 |
|
846 /** |
|
847 * Class responsible for drawing the system menu. Looks up the |
|
848 * image to draw from the Frame associated with the |
|
849 * <code>JRootPane</code>. |
|
850 */ |
|
851 @SuppressWarnings("serial") // Superclass is not serializable across versions |
|
852 private class SystemMenuBar extends JMenuBar { |
|
853 public void paint(Graphics g) { |
|
854 if (isOpaque()) { |
|
855 g.setColor(getBackground()); |
|
856 g.fillRect(0, 0, getWidth(), getHeight()); |
|
857 } |
|
858 |
|
859 if (systemIcon != null) { |
|
860 g.drawImage(systemIcon, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null); |
|
861 } else { |
|
862 Icon icon = UIManager.getIcon("InternalFrame.icon"); |
|
863 |
|
864 if (icon != null) { |
|
865 icon.paintIcon(this, g, 0, 0); |
|
866 } |
|
867 } |
|
868 } |
|
869 public Dimension getMinimumSize() { |
|
870 return getPreferredSize(); |
|
871 } |
|
872 public Dimension getPreferredSize() { |
|
873 Dimension size = super.getPreferredSize(); |
|
874 |
|
875 return new Dimension(Math.max(IMAGE_WIDTH, size.width), |
|
876 Math.max(size.height, IMAGE_HEIGHT)); |
|
877 } |
|
878 } |
|
879 |
|
880 private class TitlePaneLayout implements LayoutManager { |
|
881 public void addLayoutComponent(String name, Component c) {} |
|
882 public void removeLayoutComponent(Component c) {} |
|
883 public Dimension preferredLayoutSize(Container c) { |
|
884 int height = computeHeight(); |
|
885 return new Dimension(height, height); |
|
886 } |
|
887 |
|
888 public Dimension minimumLayoutSize(Container c) { |
|
889 return preferredLayoutSize(c); |
|
890 } |
|
891 |
|
892 private int computeHeight() { |
|
893 FontMetrics fm = rootPane.getFontMetrics(getFont()); |
|
894 int fontHeight = fm.getHeight(); |
|
895 fontHeight += 7; |
|
896 int iconHeight = 0; |
|
897 if (getWindowDecorationStyle() == JRootPane.FRAME) { |
|
898 iconHeight = IMAGE_HEIGHT; |
|
899 } |
|
900 |
|
901 int finalHeight = Math.max( fontHeight, iconHeight ); |
|
902 return finalHeight; |
|
903 } |
|
904 |
|
905 public void layoutContainer(Container c) { |
|
906 boolean leftToRight = (window == null) ? |
|
907 getRootPane().getComponentOrientation().isLeftToRight() : |
|
908 window.getComponentOrientation().isLeftToRight(); |
|
909 |
|
910 int w = getWidth(); |
|
911 int x; |
|
912 int y = 3; |
|
913 int spacing; |
|
914 int buttonHeight; |
|
915 int buttonWidth; |
|
916 |
|
917 if (closeButton != null && closeButton.getIcon() != null) { |
|
918 buttonHeight = closeButton.getIcon().getIconHeight(); |
|
919 buttonWidth = closeButton.getIcon().getIconWidth(); |
|
920 } |
|
921 else { |
|
922 buttonHeight = IMAGE_HEIGHT; |
|
923 buttonWidth = IMAGE_WIDTH; |
|
924 } |
|
925 |
|
926 // assumes all buttons have the same dimensions |
|
927 // these dimensions include the borders |
|
928 |
|
929 x = leftToRight ? w : 0; |
|
930 |
|
931 spacing = 5; |
|
932 x = leftToRight ? spacing : w - buttonWidth - spacing; |
|
933 if (menuBar != null) { |
|
934 menuBar.setBounds(x, y, buttonWidth, buttonHeight); |
|
935 } |
|
936 |
|
937 x = leftToRight ? w : 0; |
|
938 spacing = 4; |
|
939 x += leftToRight ? -spacing -buttonWidth : spacing; |
|
940 if (closeButton != null) { |
|
941 closeButton.setBounds(x, y, buttonWidth, buttonHeight); |
|
942 } |
|
943 |
|
944 if( !leftToRight ) x += buttonWidth; |
|
945 |
|
946 if (getWindowDecorationStyle() == JRootPane.FRAME) { |
|
947 if (Toolkit.getDefaultToolkit().isFrameStateSupported( |
|
948 Frame.MAXIMIZED_BOTH)) { |
|
949 if (toggleButton.getParent() != null) { |
|
950 spacing = 10; |
|
951 x += leftToRight ? -spacing -buttonWidth : spacing; |
|
952 toggleButton.setBounds(x, y, buttonWidth, buttonHeight); |
|
953 if (!leftToRight) { |
|
954 x += buttonWidth; |
|
955 } |
|
956 } |
|
957 } |
|
958 |
|
959 if (iconifyButton != null && iconifyButton.getParent() != null) { |
|
960 spacing = 2; |
|
961 x += leftToRight ? -spacing -buttonWidth : spacing; |
|
962 iconifyButton.setBounds(x, y, buttonWidth, buttonHeight); |
|
963 if (!leftToRight) { |
|
964 x += buttonWidth; |
|
965 } |
|
966 } |
|
967 } |
|
968 buttonsWidth = leftToRight ? w - x : x; |
|
969 } |
|
970 } |
|
971 |
|
972 |
|
973 |
|
974 /** |
|
975 * PropertyChangeListener installed on the Window. Updates the necessary |
|
976 * state as the state of the Window changes. |
|
977 */ |
|
978 private class PropertyChangeHandler implements PropertyChangeListener { |
|
979 public void propertyChange(PropertyChangeEvent pce) { |
|
980 String name = pce.getPropertyName(); |
|
981 |
|
982 // Frame.state isn't currently bound. |
|
983 if ("resizable".equals(name) || "state".equals(name)) { |
|
984 Frame frame = getFrame(); |
|
985 |
|
986 if (frame != null) { |
|
987 setState(frame.getExtendedState(), true); |
|
988 } |
|
989 if ("resizable".equals(name)) { |
|
990 getRootPane().repaint(); |
|
991 } |
|
992 } |
|
993 else if ("title".equals(name)) { |
|
994 repaint(); |
|
995 } |
|
996 else if ("componentOrientation" == name) { |
|
997 revalidate(); |
|
998 repaint(); |
|
999 } |
|
1000 else if ("iconImage" == name) { |
|
1001 updateSystemIcon(); |
|
1002 revalidate(); |
|
1003 repaint(); |
|
1004 } |
|
1005 } |
|
1006 } |
|
1007 |
|
1008 /** |
|
1009 * Update the image used for the system icon |
|
1010 */ |
|
1011 private void updateSystemIcon() { |
|
1012 Window window = getWindow(); |
|
1013 if (window == null) { |
|
1014 systemIcon = null; |
|
1015 return; |
|
1016 } |
|
1017 java.util.List<Image> icons = window.getIconImages(); |
|
1018 assert icons != null; |
|
1019 |
|
1020 if (icons.size() == 0) { |
|
1021 systemIcon = null; |
|
1022 } |
|
1023 else if (icons.size() == 1) { |
|
1024 systemIcon = icons.get(0); |
|
1025 } |
|
1026 else { |
|
1027 systemIcon = SunToolkit.getScaledIconImage(icons, |
|
1028 IMAGE_WIDTH, |
|
1029 IMAGE_HEIGHT); |
|
1030 } |
|
1031 } |
|
1032 |
|
1033 |
|
1034 /** |
|
1035 * WindowListener installed on the Window, updates the state as necessary. |
|
1036 */ |
|
1037 private class WindowHandler extends WindowAdapter { |
|
1038 public void windowActivated(WindowEvent ev) { |
|
1039 setActive(true); |
|
1040 } |
|
1041 |
|
1042 public void windowDeactivated(WindowEvent ev) { |
|
1043 setActive(false); |
|
1044 } |
|
1045 } |
|
1046 } |