1 /*
   2  * Copyright (c) 2006, 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 sun.tools.jconsole;
  27 
  28 import java.awt.*;
  29 import java.beans.*;
  30 import java.lang.reflect.*;
  31 
  32 import javax.swing.*;
  33 import javax.swing.border.*;
  34 import javax.swing.plaf.basic.*;
  35 
  36 
  37 /**
  38  * This class is a temporary workaround for bug 4834918:
  39  * Win L&F: JInternalFrame should merge with JMenuBar when maximized.
  40  * It is not a general solution, but intended for use within the
  41  * limited scope of JConsole when running with XP/Vista styles.
  42  */
  43 @SuppressWarnings("serial")
  44 public class MaximizableInternalFrame extends JInternalFrame {
  45     private boolean isXP;
  46     private JFrame mainFrame;
  47     private JMenuBar mainMenuBar;
  48     private String mainTitle;
  49     private JComponent titlePane;
  50     private Border normalBorder;
  51     private PropertyChangeListener pcl;
  52 
  53     public MaximizableInternalFrame(String title, boolean resizable,
  54                                     boolean closable, boolean maximizable,
  55                                     boolean iconifiable) {
  56         super(title, resizable, closable, maximizable, iconifiable);
  57 
  58         init();
  59     }
  60 
  61     private void init() {
  62         normalBorder = getBorder();
  63         isXP = normalBorder.getClass().getName().endsWith("XPBorder");
  64         if (isXP) {
  65             setRootPaneCheckingEnabled(false);
  66             titlePane = ((BasicInternalFrameUI)getUI()).getNorthPane();
  67 
  68             if (pcl == null) {
  69                 pcl = new PropertyChangeListener() {
  70                     public void propertyChange(PropertyChangeEvent ev) {
  71                         String prop = ev.getPropertyName();
  72                         if (prop.equals("icon") ||
  73                             prop.equals("maximum") ||
  74                             prop.equals("closed")) {
  75 
  76                             updateFrame();
  77                         }
  78                     }
  79                 };
  80                 addPropertyChangeListener(pcl);
  81             }
  82         } else if (pcl != null) {
  83             removePropertyChangeListener(pcl);
  84             pcl = null;
  85         }
  86     }
  87 
  88     private void updateFrame() {
  89         JFrame mainFrame;
  90         if (!isXP || (mainFrame = getMainFrame()) == null) {
  91             return;
  92         }
  93         JMenuBar menuBar = getMainMenuBar();
  94         BasicInternalFrameUI ui = (BasicInternalFrameUI)getUI();
  95         if (isMaximum() && !isIcon() && !isClosed()) {
  96             if (ui.getNorthPane() != null) {
  97                 // Merge title bar into menu bar
  98                 mainTitle = mainFrame.getTitle();
  99                 mainFrame.setTitle(mainTitle + " - " + getTitle());
 100                 if (menuBar != null) {
 101                     // Move buttons to menu bar
 102                     updateButtonStates();
 103                     menuBar.add(Box.createGlue());
 104                     for (Component c : titlePane.getComponents()) {
 105                         if (c instanceof JButton) {
 106                             menuBar.add(c);
 107                         } else if (c instanceof JLabel) {
 108                             // This is the system menu icon
 109                             menuBar.add(Box.createHorizontalStrut(3), 0);
 110                             menuBar.add(c, 1);
 111                             menuBar.add(Box.createHorizontalStrut(3), 2);
 112                         }
 113                     }
 114                     ui.setNorthPane(null);
 115                     setBorder(null);
 116                 }
 117             }
 118         } else {
 119             if (ui.getNorthPane() == null) {
 120                 // Restore title bar
 121                 mainFrame.setTitle(mainTitle);
 122                 if (menuBar != null) {
 123                     // Move buttons back to title bar
 124                     for (Component c : menuBar.getComponents()) {
 125                         if (c instanceof JButton || c instanceof JLabel) {
 126                             titlePane.add(c);
 127                         } else if (c instanceof Box.Filler) {
 128                             menuBar.remove(c);
 129                         }
 130                     }
 131                     menuBar.repaint();
 132                     updateButtonStates();
 133                     ui.setNorthPane(titlePane);
 134                     setBorder(normalBorder);
 135                 }
 136             }
 137         }
 138     }
 139 
 140     public void updateUI() {
 141         boolean isMax = (isXP && getBorder() == null);
 142         if (isMax) {
 143             try {
 144                 setMaximum(false);
 145             } catch (PropertyVetoException ex) { }
 146         }
 147         super.updateUI();
 148         init();
 149         if (isMax) {
 150             try {
 151                 setMaximum(true);
 152             } catch (PropertyVetoException ex) { }
 153         }
 154     }
 155 
 156     private JFrame getMainFrame() {
 157         if (mainFrame == null) {
 158             JDesktopPane desktop = getDesktopPane();
 159             if (desktop != null) {
 160                 mainFrame = (JFrame)SwingUtilities.getWindowAncestor(desktop);
 161             }
 162         }
 163         return mainFrame;
 164     }
 165 
 166     private JMenuBar getMainMenuBar() {
 167         if (mainMenuBar == null) {
 168             JFrame mainFrame = getMainFrame();
 169             if (mainFrame != null) {
 170                 mainMenuBar = mainFrame.getJMenuBar();
 171                 if (mainMenuBar != null &&
 172                     !(mainMenuBar.getLayout() instanceof FixedMenuBarLayout)) {
 173 
 174                     mainMenuBar.setLayout(new FixedMenuBarLayout(mainMenuBar,
 175                                                             BoxLayout.X_AXIS));
 176                 }
 177             }
 178         }
 179         return mainMenuBar;
 180     }
 181 
 182     public void setTitle(String title) {
 183         if (isXP && isMaximum()) {
 184             if (getMainFrame() != null) {
 185                 getMainFrame().setTitle(mainTitle + " - " + title);
 186             }
 187         }
 188         super.setTitle(title);
 189     }
 190 
 191 
 192     private class FixedMenuBarLayout extends BoxLayout {
 193         public FixedMenuBarLayout(Container target, int axis) {
 194             super(target, axis);
 195         }
 196 
 197         public void layoutContainer(Container target) {
 198             super.layoutContainer(target);
 199 
 200             for (Component c : target.getComponents()) {
 201                 if (c instanceof JButton) {
 202                     int y = (target.getHeight() - c.getHeight()) / 2;
 203                     c.setLocation(c.getX(), Math.max(2, y));
 204                 }
 205             }
 206         }
 207     }
 208 
 209 
 210     // The rest of this class is messy and should not be relied upon. It
 211     // uses reflection to access private, undocumented, and unsupported
 212     // classes and fields.
 213     //
 214     // Install icon wrappers to display MDI icons when the buttons
 215     // are in the menubar.
 216     //
 217     // Please note that this will very likely fail in a future version
 218     // of Swing, but at least it should fail silently.
 219     //
 220     private static Object WP_MINBUTTON, WP_RESTOREBUTTON, WP_CLOSEBUTTON,
 221                           WP_MDIMINBUTTON, WP_MDIRESTOREBUTTON, WP_MDICLOSEBUTTON;
 222     static {
 223         if (JConsole.IS_WIN) {
 224             try {
 225                 Class Part =
 226                     Class.forName("com.sun.java.swing.plaf.windows.TMSchema$Part");
 227                 if (Part != null) {
 228                     WP_MINBUTTON        = Part.getField("WP_MINBUTTON").get(null);
 229                     WP_RESTOREBUTTON    = Part.getField("WP_RESTOREBUTTON").get(null);
 230                     WP_CLOSEBUTTON      = Part.getField("WP_CLOSEBUTTON").get(null);
 231                     WP_MDIMINBUTTON     = Part.getField("WP_MDIMINBUTTON").get(null);
 232                     WP_MDIRESTOREBUTTON = Part.getField("WP_MDIRESTOREBUTTON").get(null);
 233                     WP_MDICLOSEBUTTON   = Part.getField("WP_MDICLOSEBUTTON").get(null);
 234                 }
 235 
 236                 for (String str : new String[] { "maximize", "minimize",
 237                                                  "iconify", "close" }) {
 238                     String key = "InternalFrame." + str + "Icon";
 239                     UIManager.put(key,
 240                                   new MDIButtonIcon(UIManager.getIcon(key)));
 241                 }
 242             } catch (ClassNotFoundException ex) {
 243                 if (JConsole.debug) {
 244                     ex.printStackTrace();
 245                 }
 246             } catch (NoSuchFieldException ex) {
 247                 if (JConsole.debug) {
 248                     ex.printStackTrace();
 249                 }
 250             } catch (IllegalAccessException ex) {
 251                 if (JConsole.debug) {
 252                     ex.printStackTrace();
 253                 }
 254             }
 255         }
 256     }
 257 
 258 
 259     // A wrapper class for the title pane button icons.
 260     // This code should really go in the WindowsIconsFactory class.
 261     private static class MDIButtonIcon implements Icon {
 262         Icon windowsIcon;
 263         Field part;
 264 
 265         MDIButtonIcon(Icon icon) {
 266             windowsIcon = icon;
 267 
 268             if (WP_MINBUTTON != null) {
 269                 try {
 270                     part = windowsIcon.getClass().getDeclaredField("part");
 271                     part.setAccessible(true);
 272                 } catch (NoSuchFieldException ex) {
 273                     if (JConsole.debug) {
 274                         ex.printStackTrace();
 275                     }
 276                 }
 277             }
 278         }
 279 
 280         public void paintIcon(Component c, Graphics g, int x, int y) {
 281             if (part != null) {
 282                 try {
 283                     Object v = part.get(windowsIcon);
 284 
 285                     if (c.getParent() instanceof JMenuBar) {
 286                         // Use MDI icons
 287                         if (v == WP_MINBUTTON) {
 288                             part.set(windowsIcon, WP_MDIMINBUTTON);
 289                         } else if (v == WP_RESTOREBUTTON) {
 290                             part.set(windowsIcon, WP_MDIRESTOREBUTTON);
 291                         } else if (v == WP_CLOSEBUTTON) {
 292                             part.set(windowsIcon, WP_MDICLOSEBUTTON);
 293                         }
 294                     } else {
 295                         // Use regular icons
 296                         if (v == WP_MDIMINBUTTON) {
 297                             part.set(windowsIcon, WP_MINBUTTON);
 298                         } else if (v == WP_MDIRESTOREBUTTON) {
 299                             part.set(windowsIcon, WP_RESTOREBUTTON);
 300                         } else if (v == WP_MDICLOSEBUTTON) {
 301                             part.set(windowsIcon, WP_CLOSEBUTTON);
 302                         }
 303                     }
 304                 } catch (IllegalAccessException ex) {
 305                     if (JConsole.debug) {
 306                         ex.printStackTrace();
 307                     }
 308                 }
 309             }
 310             windowsIcon.paintIcon(c, g, x, y);
 311         }
 312 
 313         public int getIconWidth(){
 314             return windowsIcon.getIconWidth();
 315         }
 316 
 317         public int getIconHeight() {
 318             return windowsIcon.getIconHeight();
 319         }
 320     }
 321 
 322 
 323     // Use reflection to invoke protected methods in BasicInternalFrameTitlePane
 324     private Method setButtonIcons;
 325     private Method enableActions;
 326 
 327     private void updateButtonStates() {
 328         try {
 329             if (setButtonIcons == null) {
 330                 Class<? extends JComponent> cls = titlePane.getClass();
 331                 Class<?> superCls = cls.getSuperclass();
 332                 setButtonIcons = cls.getDeclaredMethod("setButtonIcons");
 333                 enableActions  = superCls.getDeclaredMethod("enableActions");
 334                 setButtonIcons.setAccessible(true);
 335                 enableActions.setAccessible(true);
 336             }
 337             setButtonIcons.invoke(titlePane);
 338             enableActions.invoke(titlePane);
 339         } catch (Exception ex) {
 340             if (JConsole.debug) {
 341                 ex.printStackTrace();
 342             }
 343         }
 344     }
 345 }