001    /* JAPI - (Yet another (hopefully) useful) Java API
002     *
003     * Copyright (C) 2004-2006 Christian Hujer
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License as
007     * published by the Free Software Foundation; either version 2 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful, but
011     * WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     * General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018     * 02111-1307, USA.
019     */
020    
021    package net.sf.japi.swing;
022    
023    import java.awt.Component;
024    import java.awt.event.ActionEvent;
025    import java.util.ArrayList;
026    import java.util.List;
027    import java.util.logging.Level;
028    import java.util.logging.Logger;
029    import javax.swing.AbstractAction;
030    import javax.swing.ButtonGroup;
031    import javax.swing.JCheckBoxMenuItem;
032    import javax.swing.JDialog;
033    import javax.swing.JFrame;
034    import javax.swing.JMenu;
035    import javax.swing.JRadioButtonMenuItem;
036    import javax.swing.UIManager;
037    import static javax.swing.SwingUtilities.updateComponentTreeUI;
038    import static javax.swing.UIManager.getInstalledLookAndFeels;
039    import static javax.swing.UIManager.getLookAndFeel;
040    import static javax.swing.UIManager.installLookAndFeel;
041    import static javax.swing.UIManager.setLookAndFeel;
042    
043    /** A class that manages look and feel and provides a corresponding menu.
044     * If you want your frames and dialogs to be default look and feel decorated, you currently must invoke the corresponding method {@link
045     * #setDefaultLookAndFeelDecorated(boolean)} before creating any instances of JFrame or JDialog.
046     * @todo find a method to update the isDefaultLookAndFeelDecorated state of Frames.
047     * @todo perhaps this class should be more a component manager than just a LookAndFeelManager?
048     * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
049     */
050    public final class LookAndFeelManager {
051    
052        /** The Root Component(s) for which to update the look and feel. */
053        private final List<Component> roots = new ArrayList<Component>();
054    
055        /** Action for look and feel decoration change. */
056        private final LAFDecoAction lafDecoAction = new LAFDecoAction();
057    
058        static {
059            try {
060                installLookAndFeel("Windows", "net.sf.japi.swing.WindowsLookAndFeel");
061            } catch (final Exception e) {
062                Logger.getLogger("net.sf.japi").log(Level.INFO, "lafUnavailable", e);
063            }
064            JFrame.setDefaultLookAndFeelDecorated(true);
065            JDialog.setDefaultLookAndFeelDecorated(true);
066        }
067    
068        /** Create a LookAndFeelManager. */
069        public LookAndFeelManager() {
070        }
071    
072        /** Add a Component to the roots.
073         * @param comp Component to add to the roots
074         */
075        public void add(final Component comp) {
076            if (comp != null && !roots.contains(comp)) {
077                roots.add(comp);
078            }
079        }
080    
081        /** Provide a menu which allows the user to choose from installed look and feels.
082         * @return menu with selectable look and feels
083         */
084        public JMenu createMenu() {
085            return fillMenu(new JMenu(ActionFactory.getFactory("net.sf.japi.swing").createAction(true, "laf")));
086        }
087    
088        /** Fill a menu with look and feel selection items.
089         * @param menu Menu to fill
090         * @return menu for convenience
091         */
092        public JMenu fillMenu(final JMenu menu) {
093            final String active = getLookAndFeel().getClass().getName();
094            final ButtonGroup bg = new ButtonGroup();
095            for (final UIManager.LookAndFeelInfo lafInfo : getInstalledLookAndFeels()) {
096                final JRadioButtonMenuItem mi = new JRadioButtonMenuItem(new LAFAction(lafInfo)); // TODO: Cache LAFActions
097                if (lafInfo.getClassName().equals(active)) {
098                    mi.setSelected(true);
099                }
100                bg.add(mi);
101                menu.add(mi);
102            }
103            menu.add(lafDecoAction.createJCheckBoxMenuItem());
104            return menu;
105        }
106    
107        /** Remove a Component to the roots.
108         * @param comp Component to remove from the roots
109         */
110        public void remove(final Component comp) {
111            roots.remove(comp);
112        }
113    
114        /** Set whether JFrames and JDialogs should use the default look and feel decoration.
115         * Also updates all JFrames and JDialogs managed.
116         * @param defaultLookAndFeelDecorated <code>true</code> for decoration from default look and feel, <code>false</code> for decoration from os
117         * @see JFrame#setDefaultLookAndFeelDecorated(boolean)
118         * @see JDialog#setDefaultLookAndFeelDecorated(boolean)
119         */
120        public void setDefaultLookAndFeelDecorated(final boolean defaultLookAndFeelDecorated) {
121            JFrame.setDefaultLookAndFeelDecorated(defaultLookAndFeelDecorated);
122            JDialog.setDefaultLookAndFeelDecorated(defaultLookAndFeelDecorated);
123            for (final Component comp : roots) {
124                if (comp instanceof JFrame || comp instanceof JDialog) {
125                    updateComponentTreeUI(comp);
126                }
127            }
128        }
129    
130        /** LookAndFeel Change Action.
131         * @author $Author: christianhujer $
132         * @version $Id: LookAndFeelManager.java,v 1.8 2006/03/13 00:34:51 christianhujer Exp $
133         */
134        private final class LAFAction extends AbstractAction {
135    
136            /** Serial Version. */
137            @SuppressWarnings({"AnalyzingVariableNaming"})
138            private static final long serialVersionUID = 1L;
139    
140            /** Class name of look and feel.
141             * @serial include
142             */
143            private final String className;
144    
145            /** Create a LAFAction.
146             * @param lafInfo LookAndFeelInfo
147             */
148            LAFAction(final UIManager.LookAndFeelInfo lafInfo) {
149                putValue(NAME, lafInfo.getName());
150                className = lafInfo.getClassName();
151            }
152    
153            /** {@inheritDoc} */
154            public void actionPerformed(final ActionEvent e) {
155                //noinspection CatchGenericClass,OverlyBroadCatchBlock
156                try {
157                    setLookAndFeel(className);
158                    for (final Component comp : roots) {
159                        updateComponentTreeUI(comp);
160                    }
161                } catch (final Exception ex) {
162                    System.err.println(ex);
163                }
164            }
165    
166            /** {@inheritDoc} */
167            @Override protected Object clone() throws CloneNotSupportedException {
168                return super.clone();
169            }
170    
171        } // class LAFAction
172    
173        /** LookAndFeelDecoration Action. */
174        private final class LAFDecoAction extends AbstractAction {
175    
176            /** Serial Version. */
177            @SuppressWarnings({"AnalyzingVariableNaming"})
178            private static final long serialVersionUID = 1L;
179    
180            /** The list of JMenuItems created for this Action. */
181            private final List<JCheckBoxMenuItem> menuItems = new ArrayList<JCheckBoxMenuItem>();
182    
183            /** {@inheritDoc} */
184            public void actionPerformed(final ActionEvent e) {
185                setDefaultLookAndFeelDecorated(!JFrame.isDefaultLookAndFeelDecorated());
186            }
187    
188            /** Set the default look and feel decoration state.
189             * @param defaultLookAndFeelDecorated <code>true</code> if default look and feel decorated, otherwise <code>false</code>
190             */
191            private void setDefaultLookAndFeelDecorated(final boolean defaultLookAndFeelDecorated) {
192                LookAndFeelManager.this.setDefaultLookAndFeelDecorated(defaultLookAndFeelDecorated);
193                for (final JCheckBoxMenuItem menuItem : menuItems) {
194                    menuItem.setSelected(defaultLookAndFeelDecorated);
195                }
196            }
197    
198            /** Create a JCheckBoxMenuItem.
199             * @return JCHeckBoxMenuItem for the associated look and feel
200             */
201            public JCheckBoxMenuItem createJCheckBoxMenuItem() {
202                final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(this);
203                menuItems.add(menuItem);
204                return menuItem;
205            }
206    
207            /** {@inheritDoc} */
208            @Override protected Object clone() throws CloneNotSupportedException {
209                return super.clone();
210            }
211    
212        } // class LAFDecoAction
213    
214    } // class LookAndFeelManager