001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Vadim L. Bogdanov
020: * @version $Revision$
021: */package javax.swing.plaf.basic;
022:
023: import java.awt.Color;
024: import java.awt.Component;
025: import java.awt.Container;
026: import java.awt.Dimension;
027: import java.awt.FontMetrics;
028: import java.awt.Graphics;
029: import java.awt.LayoutManager;
030: import java.awt.Rectangle;
031:
032: import java.awt.event.ActionEvent;
033: import java.awt.event.HierarchyEvent;
034: import java.awt.event.HierarchyListener;
035:
036: import java.beans.PropertyChangeEvent;
037: import java.beans.PropertyChangeListener;
038: import java.beans.PropertyVetoException;
039:
040: import javax.swing.AbstractAction;
041: import javax.swing.Action;
042: import javax.swing.BorderFactory;
043: import javax.swing.Icon;
044: import javax.swing.JButton;
045: import javax.swing.JComponent;
046: import javax.swing.JInternalFrame;
047: import javax.swing.JMenu;
048: import javax.swing.JMenuBar;
049: import javax.swing.SwingConstants;
050: import javax.swing.SwingUtilities;
051: import javax.swing.UIManager;
052:
053: import javax.swing.border.EmptyBorder;
054: import javax.swing.event.InternalFrameEvent;
055: import javax.swing.event.InternalFrameListener;
056:
057: import org.apache.harmony.x.swing.StringConstants;
058: import org.apache.harmony.x.swing.TitlePaneInternals;
059: import org.apache.harmony.x.swing.Utilities;
060:
061: public class BasicInternalFrameTitlePane extends JComponent {
062:
063: public class CloseAction extends AbstractAction {
064: public CloseAction() {
065: putValue(SHORT_DESCRIPTION, closeTooltipText);
066: putValue(SMALL_ICON, closeIcon);
067: }
068:
069: public void actionPerformed(final ActionEvent e) {
070: if (frame.isClosable()) {
071: try {
072: frame.setClosed(true);
073: } catch (final PropertyVetoException v) {
074: }
075: }
076: }
077: }
078:
079: public class IconifyAction extends AbstractAction {
080: public IconifyAction() {
081: putValue(SHORT_DESCRIPTION, minimizeTooltipText);
082: putValue(SMALL_ICON, iconIcon);
083: }
084:
085: public void actionPerformed(final ActionEvent e) {
086: if (frame.isIconifiable()) {
087: try {
088: frame.setIcon(true);
089: } catch (final PropertyVetoException v) {
090: }
091: }
092: }
093: }
094:
095: public class MaximizeAction extends AbstractAction {
096: public MaximizeAction() {
097: putValue(SHORT_DESCRIPTION, maximizeTooltipText);
098: putValue(SMALL_ICON, maxIcon);
099: }
100:
101: public void actionPerformed(final ActionEvent e) {
102: if (frame.isMaximizable()) {
103: try {
104: if (frame.isIcon()) {
105: frame.setIcon(false);
106: }
107: frame.setMaximum(true);
108: } catch (final PropertyVetoException v) {
109: }
110: }
111: }
112: }
113:
114: public class RestoreAction extends AbstractAction {
115: public RestoreAction() {
116: putValue(SHORT_DESCRIPTION, restoreTooltipText);
117: putValue(SMALL_ICON, minIcon);
118: }
119:
120: public void actionPerformed(final ActionEvent e) {
121: if (frame.isIcon()) {
122: if (frame.isIconifiable()) {
123: try {
124: frame.setIcon(false);
125: } catch (final PropertyVetoException v) {
126: }
127: }
128: } else if (frame.isMaximum()) {
129: if (frame.isMaximizable()) {
130: try {
131: frame.setMaximum(false);
132: } catch (final PropertyVetoException v) {
133: }
134: }
135: }
136: }
137: }
138:
139: public class MoveAction extends AbstractAction {
140: private static final String MOVE_ACTION_NAME = "Move";
141:
142: public MoveAction() {
143: putValue(Action.NAME, MOVE_ACTION_NAME);
144: }
145:
146: public void actionPerformed(final ActionEvent e) {
147: if (e == null) {
148: return;
149: }
150:
151: ((BasicDesktopPaneUI) frame.getDesktopPane().getUI()).frameOperation = BasicDesktopPaneUI.DRAGGING;
152: }
153: }
154:
155: public class SizeAction extends AbstractAction {
156: private static final String SIZE_ACTION_NAME = "Size";
157:
158: public SizeAction() {
159: putValue(Action.NAME, SIZE_ACTION_NAME);
160: }
161:
162: public void actionPerformed(final ActionEvent e) {
163: if (e == null) {
164: return;
165: }
166: ((BasicDesktopPaneUI) frame.getDesktopPane().getUI()).frameOperation = BasicDesktopPaneUI.RESIZING;
167: }
168: }
169:
170: public class PropertyChangeHandler implements
171: PropertyChangeListener {
172: public void propertyChange(final PropertyChangeEvent e) {
173: // Note: could optimize work with buttons
174: // may be we should always do the same work (update buttons,
175: // enableActions, etc.)?
176: if (JInternalFrame.IS_MAXIMUM_PROPERTY.equals(e
177: .getPropertyName())
178: || JInternalFrame.IS_ICON_PROPERTY.equals(e
179: .getPropertyName())) {
180: setButtonIcons();
181: enableActions();
182: } else if (e.getPropertyName().equals(
183: StringConstants.INTERNAL_FRAME_ICONABLE_PROPERTY)) {
184: updateButton(iconButton, frame.isIconifiable());
185: } else if (e
186: .getPropertyName()
187: .equals(
188: StringConstants.INTERNAL_FRAME_MAXIMIZABLE_PROPERTY)) {
189: updateButton(maxButton, frame.isMaximizable());
190: } else if (e.getPropertyName().equals(
191: StringConstants.INTERNAL_FRAME_CLOSABLE_PROPERTY)) {
192: updateButton(closeButton, frame.isClosable());
193: } else if (e.getPropertyName().equals(
194: StringConstants.INTERNAL_FRAME_RESIZABLE_PROPERTY)) {
195: enableActions();
196: } else if (e.getPropertyName().equals("ancestor")) {
197: // to enable sizeAction, moveAction
198: enableActions();
199: revalidate();
200: } else if (JInternalFrame.FRAME_ICON_PROPERTY.equals(e
201: .getPropertyName())) {
202: internals.setFrameIcon(frame.getFrameIcon());
203: revalidate();
204: repaint();
205: } else if (JInternalFrame.TITLE_PROPERTY.equals(e
206: .getPropertyName())) {
207: revalidate();
208: repaint();
209: }
210: }
211:
212: private void updateButton(final JButton button,
213: final boolean add) {
214: if (add) {
215: add(button);
216: } else {
217: remove(button);
218: }
219: enableActions();
220: revalidate();
221: repaint();
222: }
223: }
224:
225: public class SystemMenuBar extends JMenuBar {
226: public SystemMenuBar() {
227: setFocusable(false);
228: setBorder(new EmptyBorder(0, 0, 0, 0));
229: }
230:
231: public void paint(final Graphics g) {
232: paintChildren(g);
233: }
234: }
235:
236: public class TitlePaneLayout implements LayoutManager {
237: public Dimension preferredLayoutSize(final Container c) {
238: return minimumLayoutSize(c);
239: }
240:
241: public Dimension minimumLayoutSize(final Container c) {
242: Dimension size = Utilities.getCompoundLabelSize(
243: BasicInternalFrameTitlePane.this ,
244: getMinimumString(internals.getWindowTitle()),
245: internals.getFrameIcon(), SwingConstants.LEFT,
246: SwingConstants.LEFT, internals.gapX);
247:
248: if (closeButton.getParent() == BasicInternalFrameTitlePane.this ) {
249: size.width += internals.gapX + closeIcon.getIconWidth();
250: }
251: if (maxButton.getParent() == BasicInternalFrameTitlePane.this ) {
252: size.width += internals.gapX + maxIcon.getIconWidth();
253: }
254: if (iconButton.getParent() == BasicInternalFrameTitlePane.this ) {
255: size.width += internals.gapX + iconIcon.getIconWidth();
256: }
257:
258: size.width += 2 * internals.gapX;
259:
260: return Utilities.addInsets(size,
261: BasicInternalFrameTitlePane.this .getInsets());
262: }
263:
264: private String getMinimumString(final String str) {
265: if (str == null) {
266: return "";
267: }
268: return str.substring(0, Math.min(3, str.length())) + "...";
269: }
270:
271: public void addLayoutComponent(final String name,
272: final Component c) {
273: }
274:
275: public void layoutContainer(final Container c) {
276: final int width = internals.getUIFrameIcon().getIconWidth();
277: final int height = internals.getUIFrameIcon()
278: .getIconHeight();
279:
280: Rectangle inner = SwingUtilities.calculateInnerArea(
281: BasicInternalFrameTitlePane.this , null);
282: int y = inner.y + (inner.height - height) / 2;
283: boolean isLTR = getComponentOrientation().isLeftToRight();
284: int buttonOffsetX = isLTR ? getWidth() : -width;
285: int sign = isLTR ? 1 : -1;
286: Rectangle buttonBounds = new Rectangle(buttonOffsetX, y,
287: width, height);
288:
289: // calculate bounds of closeButton
290: if (closeButton.getParent() == BasicInternalFrameTitlePane.this ) {
291: buttonBounds.x -= sign * (width + internals.gapX);
292: closeButton.setBounds(buttonBounds);
293: }
294:
295: // calculate bounds of maxButton
296: if (maxButton.getParent() == BasicInternalFrameTitlePane.this ) {
297: buttonBounds.x -= sign * (width + internals.gapX);
298: maxButton.setBounds(buttonBounds);
299: }
300:
301: // calculate bounds of iconButton
302: if (iconButton.getParent() == BasicInternalFrameTitlePane.this ) {
303: buttonBounds.x -= sign * (width + internals.gapX);
304: iconButton.setBounds(buttonBounds);
305: }
306:
307: int iconTextOffsetX = isLTR ? internals.gapX
308: : buttonBounds.x + width + internals.gapX;
309: int reminderWidth = isLTR ? buttonBounds.x - 2
310: * internals.gapX : getWidth() - iconTextOffsetX
311: - internals.gapX;
312: Rectangle viewR = new Rectangle(iconTextOffsetX, 0,
313: reminderWidth, getHeight());
314:
315: paintedTitle = layoutCompoundLabel(
316: BasicInternalFrameTitlePane.this , internals
317: .getWindowTitle(),
318: internals.hasMenuBar ? internals.getFrameIcon()
319: : null, viewR, menuBarBounds, textR);
320: if (menuBar != null) {
321: menuBar.setBounds(menuBarBounds);
322: }
323:
324: // calculate bounds of possible decoration
325: if (internals.decorationR != null) {
326: int decorX = isLTR ? textR.x + textR.width
327: + internals.gapX : buttonBounds.x + width
328: + internals.gapX;
329: int decorWidth = isLTR ? viewR.x + viewR.width - decorX
330: : textR.x - viewR.x - internals.gapX;
331:
332: internals.decorationR.setBounds(decorX, textR.y,
333: decorWidth, textR.height);
334: }
335: }
336:
337: private String layoutCompoundLabel(final JComponent c,
338: final String text, final Icon icon,
339: final Rectangle viewR, final Rectangle iconR,
340: final Rectangle textR) {
341: boolean isEmptyTitle = Utilities.isEmptyString(text);
342: String fixedText = isEmptyTitle ? " " : text;
343:
344: String result = SwingUtilities.layoutCompoundLabel(c,
345: Utilities.getFontMetrics(c), fixedText, icon,
346: SwingConstants.CENTER, SwingConstants.LEADING,
347: SwingConstants.CENTER, SwingConstants.TRAILING,
348: viewR, iconR, textR, internals.gapX);
349:
350: if (isEmptyTitle) {
351: result = "";
352: textR.width = -internals.gapX;
353: }
354: return result;
355: }
356:
357: public void removeLayoutComponent(final Component comp) {
358: }
359: }
360:
361: protected static final String ICONIFY_CMD = "Iconify";
362: protected static final String MAXIMIZE_CMD = "Maximize";
363: protected static final String RESTORE_CMD = "Restore";
364: protected static final String CLOSE_CMD = "Close";
365: protected static final String MOVE_CMD = "Move";
366: protected static final String SIZE_CMD = "Size";
367:
368: protected JMenuBar menuBar;
369: protected JMenu windowMenu;
370: protected JInternalFrame frame;
371:
372: protected Color selectedTitleColor;
373: protected Color notSelectedTitleColor;
374: protected Color selectedTextColor;
375: protected Color notSelectedTextColor;
376:
377: protected JButton iconButton;
378: protected JButton maxButton;
379: protected JButton closeButton;
380:
381: protected Icon iconIcon;
382: protected Icon maxIcon;
383: protected Icon minIcon;
384: protected Icon closeIcon;
385:
386: protected Action iconifyAction;
387: protected Action maximizeAction;
388: protected Action restoreAction;
389: protected Action closeAction;
390: protected Action moveAction;
391: protected Action sizeAction;
392:
393: protected PropertyChangeListener propertyChangeListener;
394:
395: // tooltips text
396: private String maximizeTooltipText;
397: private String minimizeTooltipText;
398: private String closeTooltipText;
399: private String restoreTooltipText;
400:
401: // system menu items text
402: private String closeButtonText;
403: private String maxButtonText;
404: private String minButtonText;
405: private String moveButtonText;
406: private String restoreButtonText;
407: private String sizeButtonText;
408:
409: private String paintedTitle;
410: private Rectangle textR = new Rectangle();
411: private Rectangle menuBarBounds = new Rectangle();
412:
413: private TitlePaneInternals internals;
414:
415: public BasicInternalFrameTitlePane(final JInternalFrame frame) {
416: if (frame == null) {
417: throw new NullPointerException();
418: }
419:
420: this .frame = frame;
421: setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0));
422: installInternals();
423: installTitlePane();
424: }
425:
426: protected JMenuBar createSystemMenuBar() {
427: return new SystemMenuBar();
428: }
429:
430: protected void assembleSystemMenu() {
431: windowMenu = createSystemMenu();
432: internals.setWindowMenu(windowMenu);
433: addSystemMenuItems(windowMenu);
434:
435: menuBar = createSystemMenuBar();
436: menuBar.add(windowMenu);
437: }
438:
439: protected void addSystemMenuItems(final JMenu menu) {
440: menu.add(TitlePaneInternals.createMenuItem(restoreAction,
441: restoreButtonText, 'R'));
442: menu.add(TitlePaneInternals.createMenuItem(moveAction,
443: moveButtonText, 'M'));
444: menu.add(TitlePaneInternals.createMenuItem(sizeAction,
445: sizeButtonText, 'S'));
446: menu.add(TitlePaneInternals.createMenuItem(iconifyAction,
447: minButtonText, 'N'));
448: menu.add(TitlePaneInternals.createMenuItem(maximizeAction,
449: maxButtonText, 'X'));
450:
451: menu.addSeparator();
452: menu.add(TitlePaneInternals.createMenuItem(closeAction,
453: closeButtonText, 'C'));
454: }
455:
456: protected JMenu createSystemMenu() {
457: JMenu menu = new JMenu();
458: menu.setBorder(new EmptyBorder(0, 0, 0, 0));
459: menu.setOpaque(false);
460: return menu;
461: }
462:
463: protected void postClosingEvent(final JInternalFrame frame) {
464: // this method seems to be unused
465: Object[] listeners = frame
466: .getListeners(InternalFrameListener.class);
467: InternalFrameEvent e = null;
468:
469: for (int i = 0; i < listeners.length; i++) {
470: if (e == null) {
471: e = new InternalFrameEvent(frame,
472: InternalFrameEvent.INTERNAL_FRAME_CLOSING);
473: }
474: ((InternalFrameListener) listeners[i])
475: .internalFrameClosing(e);
476: }
477: }
478:
479: protected PropertyChangeListener createPropertyChangeListener() {
480: return new PropertyChangeHandler();
481: }
482:
483: protected LayoutManager createLayout() {
484: return new TitlePaneLayout();
485: }
486:
487: protected String getTitle(final String text, final FontMetrics fm,
488: final int availableWidth) {
489: return Utilities.clipString(fm, text, availableWidth);
490: }
491:
492: protected void paintTitleBackground(final Graphics g) {
493: g.setColor(internals.isSelected() ? selectedTitleColor
494: : notSelectedTitleColor);
495: g.fillRect(0, 0, getWidth(), getHeight());
496: }
497:
498: public void paintComponent(final Graphics g) {
499: paintTitleBackground(g);
500:
501: if (menuBar == null) {
502: Icon frameIcon = internals.getFrameIcon();
503: if (frameIcon != null) {
504: frameIcon.paintIcon(this , g, menuBarBounds.x,
505: menuBarBounds.y);
506: }
507: }
508:
509: g.setColor(internals.isSelected() ? selectedTextColor
510: : notSelectedTextColor);
511:
512: FontMetrics fm = getFontMetrics(g.getFont());
513: g.drawString(paintedTitle, textR.x, Utilities.getTextY(fm,
514: textR));
515: }
516:
517: protected void installListeners() {
518: if (propertyChangeListener == null) {
519: propertyChangeListener = createPropertyChangeListener();
520: }
521: frame.addPropertyChangeListener(propertyChangeListener);
522:
523: // propertyChangeListener will be uninstalled
524: // when title pane is removed from internal frame
525: addHierarchyListener(new HierarchyListener() {
526: public void hierarchyChanged(final HierarchyEvent e) {
527: if (e.getChanged() == BasicInternalFrameTitlePane.this
528: && (e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0
529: && !frame
530: .isAncestorOf(BasicInternalFrameTitlePane.this )) {
531: uninstallListeners();
532: }
533: }
534: });
535: }
536:
537: protected void uninstallListeners() {
538: // Warning: there is no method like uninstallTitlePane(); this method
539: // is called directly from HierarchyListener.hierarchyChanged();
540: frame.removePropertyChangeListener(propertyChangeListener);
541: }
542:
543: protected void installDefaults() {
544: // tooltips
545: maximizeTooltipText = UIManager
546: .getString("InternalFrame.maxButtonToolTip");
547: minimizeTooltipText = UIManager
548: .getString("InternalFrame.iconButtonToolTip");
549: closeTooltipText = UIManager
550: .getString("InternalFrame.closeButtonToolTip");
551: restoreTooltipText = UIManager
552: .getString("InternalFrame.restoreButtonToolTip");
553:
554: // window menu labels
555: closeButtonText = UIManager
556: .getString("InternalFrameTitlePane.closeButtonText");
557: maxButtonText = UIManager
558: .getString("InternalFrameTitlePane.maximizeButtonText");
559: minButtonText = UIManager
560: .getString("InternalFrameTitlePane.minimizeButtonText");
561: moveButtonText = UIManager
562: .getString("InternalFrameTitlePane.moveButtonText");
563: restoreButtonText = UIManager
564: .getString("InternalFrameTitlePane.restoreButtonText");
565: sizeButtonText = UIManager
566: .getString("InternalFrameTitlePane.sizeButtonText");
567:
568: // icons
569: closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
570: maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
571: minIcon = UIManager.getIcon("InternalFrame.minimizeIcon");
572: iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
573: // "InternalFrame.icon" is loaded in BasicInternalFrameUI
574:
575: // colors
576: selectedTitleColor = UIManager
577: .getColor("InternalFrame.activeTitleBackground");
578: selectedTextColor = UIManager
579: .getColor("InternalFrame.activeTitleForeground");
580: notSelectedTitleColor = UIManager
581: .getColor("InternalFrame.inactiveTitleBackground");
582: notSelectedTextColor = UIManager
583: .getColor("InternalFrame.inactiveTitleForeground");
584:
585: // font
586: setFont(UIManager.getFont("InternalFrame.titleFont"));
587: }
588:
589: protected void uninstallDefaults() {
590: // there is no need to uninstall anything because
591: // the title pane is replaced while changing L&F
592: }
593:
594: /**
595: * This function is used in
596: * <code>BasicInternalFrameUI.installKeyboardActions</code> to implement
597: * menu opening with keyboard.
598: */
599: protected void showSystemMenu() {
600: windowMenu.doClick(0);
601: }
602:
603: protected void setButtonIcons() {
604: if (frame.isIcon()) {
605: iconButton.setAction(restoreAction);
606: } else {
607: iconButton.setAction(iconifyAction);
608: }
609:
610: if (frame.isMaximum() && !frame.isIcon()) {
611: maxButton.setAction(restoreAction);
612: } else {
613: maxButton.setAction(maximizeAction);
614: }
615: }
616:
617: protected void installTitlePane() {
618: installDefaults();
619: installListeners();
620: createActions();
621: setLayout(createLayout());
622: createButtons();
623: assembleSystemMenu();
624: addSubComponents();
625: enableActions();
626: }
627:
628: protected void enableActions() {
629: iconifyAction.setEnabled(!frame.isIcon()
630: && frame.isIconifiable());
631: closeAction.setEnabled(frame.isClosable());
632: maximizeAction.setEnabled(frame.isMaximizable()
633: && (!frame.isMaximum() || frame.isIcon()
634: && frame.isIconifiable()));
635:
636: restoreAction.setEnabled(frame.isMaximum()
637: && frame.isMaximizable() || frame.isIcon()
638: && frame.isIconifiable());
639:
640: moveAction
641: .setEnabled(frame.getDesktopPane() != null
642: && !frame.isMaximum()
643: && frame.getDesktopPane().getUI() instanceof BasicDesktopPaneUI);
644:
645: sizeAction.setEnabled(moveAction.isEnabled()
646: && frame.isResizable() && !frame.isIcon());
647: }
648:
649: protected void createButtons() {
650: maxButton = createTitlePaneButton(maximizeAction);
651: iconButton = createTitlePaneButton(iconifyAction);
652: closeButton = createTitlePaneButton(closeAction);
653:
654: setButtonIcons();
655: }
656:
657: private JButton createTitlePaneButton(final Action action) {
658: JButton button = new JButton(action);
659: button.setFocusable(false);
660: return button;
661: }
662:
663: protected void createActions() {
664: closeAction = new CloseAction();
665: iconifyAction = new IconifyAction();
666: maximizeAction = new MaximizeAction();
667: moveAction = new MoveAction();
668: restoreAction = new RestoreAction();
669: sizeAction = new SizeAction();
670: }
671:
672: protected void addSubComponents() {
673: if (menuBar != null) {
674: add(menuBar);
675: }
676:
677: if (frame.isIconifiable()) {
678: add(iconButton);
679: }
680: if (frame.isMaximizable()) {
681: add(maxButton);
682: }
683: if (frame.isClosable()) {
684: add(closeButton);
685: }
686: }
687:
688: private void installInternals() {
689: internals = new TitlePaneInternals(frame);
690: putClientProperty("internals", internals);
691: }
692: }
|