View Javadoc

1   package com.melloware.jukes.gui.view.preferences;
2   
3   import java.awt.Component;
4   import java.beans.PropertyChangeEvent;
5   import java.beans.PropertyChangeListener;
6   import java.util.ArrayList;
7   import java.util.Collections;
8   import java.util.Comparator;
9   import java.util.logging.Logger;
10  import java.util.prefs.Preferences;
11  
12  import javax.swing.DefaultListCellRenderer;
13  import javax.swing.JList;
14  import javax.swing.JPanel;
15  import javax.swing.JScrollPane;
16  import javax.swing.ListModel;
17  import javax.swing.ListSelectionModel;
18  import javax.swing.LookAndFeel;
19  import javax.swing.UIManager;
20  import javax.swing.UnsupportedLookAndFeelException;
21  import javax.swing.event.ListSelectionEvent;
22  import javax.swing.event.ListSelectionListener;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  import com.jgoodies.binding.list.ArrayListModel;
28  import com.jgoodies.binding.value.ValueModel;
29  import com.jgoodies.forms.builder.PanelBuilder;
30  import com.jgoodies.forms.layout.CellConstraints;
31  import com.jgoodies.forms.layout.FormLayout;
32  import com.jgoodies.looks.plastic.PlasticLookAndFeel;
33  import com.jgoodies.looks.plastic.PlasticTheme;
34  import com.jgoodies.uif.application.Application;
35  import com.jgoodies.uif.laf.ExtUIManager;
36  import com.jgoodies.uif.laf.LookConfiguration;
37  import com.jgoodies.uif.laf.LookConfigurations;
38  import com.jgoodies.uif.lazy.Preparable;
39  import com.melloware.jukes.gui.tool.Resources;
40  import com.melloware.jukes.util.MessageUtil;
41  
42  
43  /**
44   * A panel for choosing a <code>LookAndFeel</code> and other
45   * look configuration settings, e.g. a color theme.<p>
46   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
47   * @author Emil A. Lefkof III <info@melloware.com>
48   * @version 4.0
49   * 
50   * @see ListModel
51   * @see ArrayListModel
52   */
53  
54  public final class LookAndFeelPanel extends JPanel {
55     /**
56      * Logger for this class
57      */
58     private static final Log LOG = LogFactory.getLog(LookAndFeelPanel.class);
59  
60      private static final ListModel PLASTIC_THEMES_MODEL =
61          new ArrayListModel(PlasticLookAndFeel.getInstalledThemes());
62  
63      private static final ListModel NO_THEMES_MODEL =
64          new ArrayListModel();
65  
66  
67      private static ListModel supportedLookAndFeelInstances;
68  
69      private final LookConfigurations configurations;
70  
71      private JList lafList;
72      private JList themesList;
73      private transient ListSelectionListener themeListener;
74      private LookAndFeelPreviewPanel previewPanel;
75      private boolean isLookChanged = false;
76  
77      
78      // Instance Creation ******************************************************
79      
80      /**
81       * Constructs a <code>LookAndFeelPanel</code> using the given
82       * apply trigger, which triggers the UI update.
83       * 
84       * @param triggerChannel    changes to <code>Boolean.TRUE</code> or 
85       *      <code>Boolean.FALSE</code> to indicate a commit or flush event
86       */
87      public LookAndFeelPanel(ValueModel triggerChannel) {
88          this.configurations = getClonedLookConfigurations();
89          initComponents();
90          build();
91          registerListeners();
92          triggerChannel.addValueChangeListener(new CommitHandler());
93      }
94  
95      private static LookConfigurations getClonedLookConfigurations() {
96          Object storedValue =
97              ExtUIManager.getLookConfigurations();
98          LookConfigurations storedConfigs = (LookConfigurations) storedValue;
99          return (LookConfigurations) storedConfigs.clone();
100     }
101 
102     // Public API ***********************************************************
103 
104     /**
105      * Ensures that the preselections in the look-and-feel list
106      * and in the themes list are visible.
107      */
108     public void ensureSelectionsAreVisible() {
109         lafList.ensureIndexIsVisible(lafList.getSelectedIndex());
110         themesList.ensureIndexIsVisible(themesList.getSelectedIndex());
111     }
112 
113     // Building *************************************************************
114 
115     private void initComponents() {
116         lafList = new JList();
117         lafList.setModel(getSupportedLookAndFeelInstances());
118         lafList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
119         lafList.setCellRenderer(new LooksListCellRenderer());
120         selectLook(configurations.getDefaultConfiguration().getLookAndFeel());
121 
122         themesList = new JList();
123         updateThemesModel();
124         selectTheme(getSelectedConfiguration().getTheme());
125         themesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
126         themesList.setCellRenderer(new ThemesListCellRenderer());
127 
128         previewPanel = new LookAndFeelPreviewPanel();
129     }
130     
131     /**
132      * Builds the panel: look and feel panel on top, and the preview at the bottom.
133      */
134     private void build() {
135         FormLayout fl = new FormLayout(
136                 "fill:default:grow, 4dlu, fill:default:grow", 
137                 "pref, 1dlu, fill:100dlu, 9dlu, pref, 1dlu, pref");
138         fl.setColumnGroups(new int[][]{{1, 3}});
139         fl.setRowGroups(new int[][]{{3, 7}});
140         
141         PanelBuilder builder = new PanelBuilder(fl, this);
142         builder.setDefaultDialogBorder();
143         CellConstraints cc = new CellConstraints();
144 
145         builder.addTitle(Resources.getString("label.LookFeel"),         cc.xy (1, 1));
146         builder.addTitle(Resources.getString("label.Theme"),                cc.xy (3, 1));
147         builder.add(new JScrollPane(lafList),    cc.xy (1, 3));
148         builder.add(new JScrollPane(themesList), cc.xy (3, 3));
149         builder.addTitle(Resources.getString("label.Preview"),              cc.xyw(1, 5, 3));
150         builder.add(previewPanel,                cc.xyw(1, 7, 3));
151     }
152 
153     /**
154      * Registers listeners to respond to selections in the
155      * look-and-feel and themes lists.
156      */
157     private void registerListeners() {
158         // Listen to selection events.
159         lafList.addListSelectionListener(new ListSelectionListener() {
160             public void valueChanged(ListSelectionEvent e) {
161                 if (!e.getValueIsAdjusting())
162                     changedLookAndFeel();
163             }
164         });
165         themeListener = new ListSelectionListener() {
166             public void valueChanged(ListSelectionEvent e) {
167                 if (!e.getValueIsAdjusting())
168                     changedTheme();
169             }
170         };
171         // Listen to selection changes
172         themesList.addListSelectionListener(themeListener);
173     }
174 
175     // Updating *************************************************************
176 
177     /**
178      * The look and feel selection changed; performs the appropriate update actions.
179      */
180     private void changedLookAndFeel() {
181         isLookChanged = true; 
182         // Get the (new) selected look and feel.
183         //LookAndFeel laf = getSelectedLookAndFeel();
184         // Make it the selected configuration.
185         //configurations.getDefaultConfiguration().
186         	MessageUtil.showInformation(this, Resources.getString("messages.lookfeel")); //AZ
187 
188         // Update the themes list without firing the change listener.
189         themesList.removeListSelectionListener(themeListener);
190         updateThemesModel();
191         selectTheme(getSelectedConfiguration().getTheme());
192         themesList.addListSelectionListener(themeListener);
193 
194         updatePreviewPanel();
195     }
196 
197     /**
198      * The theme selection changed; performs the appropriate update actions.
199      */
200     private void changedTheme() {
201         LookAndFeel laf = getSelectedLookAndFeel();
202         if (!(laf instanceof PlasticLookAndFeel))
203             return;
204         putConfiguration(createLookConfiguration());
205         updatePreviewPanel();
206     }
207 
208     /**
209      * Selects the specified <code>LookAndFeel</code>.
210      */
211     private void selectLook(LookAndFeel selectedLook) {
212         String lafClassName = selectedLook.getClass().getName();
213         ListModel model = lafList.getModel();
214         for (int i = 0; i < model.getSize(); i++) {
215             LookAndFeel laf = (LookAndFeel) model.getElementAt(i);
216             if (lafClassName.equals(laf.getClass().getName())) {
217                 lafList.setSelectedIndex(i);
218                 lafList.ensureIndexIsVisible(i);
219                 break;
220             }
221         }
222     }
223 
224     /**
225      * Selects the specified theme.
226      */
227     private void selectTheme(Object selectedTheme) {
228         if (selectedTheme == null)
229             return;
230         String themeClassName = selectedTheme.getClass().getName();
231         ListModel model = themesList.getModel();
232         for (int i = 0; i < model.getSize(); i++) {
233             PlasticTheme theme = (PlasticTheme) model.getElementAt(i);
234             if (themeClassName.equals(theme.getClass().getName())) {
235                 themesList.setSelectedIndex(i);
236                 themesList.ensureIndexIsVisible(i);
237                 break;
238             }
239         }
240     }
241 
242     /**
243      * Updates the preview panel.
244      */
245     private void updatePreviewPanel() {
246         try {
247             // Save the old L&F.
248             LookAndFeel oldLaf = UIManager.getLookAndFeel();
249             LookAndFeel selectedLaF = getSelectedLookAndFeel();
250             PlasticTheme oldTheme = null;
251 
252             if (selectedLaF instanceof PlasticLookAndFeel) {
253                 oldTheme = (PlasticTheme) PlasticLookAndFeel.getPlasticTheme(); 
254                 PlasticTheme theme = getSelectedTheme();
255                 if (theme != null)
256                     PlasticLookAndFeel.setPlasticTheme(theme);
257             }
258 
259             UIManager.setLookAndFeel(selectedLaF);
260             previewPanel.updateAndValidate();
261 
262             if (selectedLaF instanceof PlasticLookAndFeel)
263                 PlasticLookAndFeel.setPlasticTheme(oldTheme);
264 
265             // Restore the old L&F.
266             UIManager.setLookAndFeel(oldLaf);
267             previewPanel.updateComponentTree();
268 
269         } catch (UnsupportedLookAndFeelException e) {
270             // Ignore unsupported looks
271         }
272     }
273 
274     /**
275      * Updates the themes model.
276      */
277     private void updateThemesModel() {
278         boolean supportsThemes =
279             getSelectedLookAndFeel() instanceof PlasticLookAndFeel;
280 
281         ListModel newModel = supportsThemes 
282                                 ? PLASTIC_THEMES_MODEL 
283                                 : NO_THEMES_MODEL;
284         if (themesList.getModel() != newModel)
285             themesList.setModel(newModel);
286 
287         themesList.setEnabled(supportsThemes);
288     }
289 
290     // Accessing the look and feel configurations ***************************
291 
292     private LookConfiguration getSelectedConfiguration() {
293         return configurations.getDefaultConfiguration();
294     }
295 
296     @SuppressWarnings("unchecked")
297     private void putConfiguration(LookConfiguration config) {
298         configurations.getConfigurations().add(config);
299         configurations.setDefaultConfiguration(config);
300     }
301 
302     private LookConfiguration createLookConfiguration() {
303         LookAndFeel laf = getSelectedLookAndFeel();
304         Object theme =
305             laf instanceof PlasticLookAndFeel ? getSelectedTheme() : null;
306         return new LookConfiguration(laf, theme);
307     }
308 
309     private LookAndFeel getSelectedLookAndFeel() {
310         return (LookAndFeel) lafList.getSelectedValue();
311     }
312 
313     private PlasticTheme getSelectedTheme() {
314         return (PlasticTheme) themesList.getSelectedValue();
315     }
316 
317     // Computing the Supported Looks ****************************************
318 
319     /**
320      * Answers a cached and lazily instantiated list of supported look and feels.
321      */
322     private synchronized static ListModel getSupportedLookAndFeelInstances() {
323         if (supportedLookAndFeelInstances == null) {
324             supportedLookAndFeelInstances =
325                 new ArrayListModel(computeSupportedLookAndFeelInstances());
326         }
327         return supportedLookAndFeelInstances;
328     }
329 
330     /**
331      * Computes the list of supported look and feels.
332      */
333     @SuppressWarnings("unchecked")
334     private static java.util.List computeSupportedLookAndFeelInstances() {
335         UIManager.LookAndFeelInfo[] lafInfos =
336             UIManager.getInstalledLookAndFeels();
337         java.util.List result = new ArrayList(lafInfos.length);
338         for (int i = 0; i < lafInfos.length; i++) {
339             String className = lafInfos[i].getClassName();
340             LookAndFeel laf =
341                 ExtUIManager.createLookAndFeelInstance(className);
342             if ((laf != null) && (laf.isSupportedLookAndFeel()))
343                 result.add(laf);
344         }
345         Collections.sort(result, new Comparator() {
346             public int compare(Object o1, Object o2) {
347                 LookAndFeel laf1 = (LookAndFeel) o1;
348                 LookAndFeel laf2 = (LookAndFeel) o2;
349                 return laf1.getName().toUpperCase().compareTo(
350                     laf2.getName().toUpperCase());
351             }
352         });
353         return result;
354     }
355 
356     // Inner Helper Classes ***************************************************
357     
358     private class CommitHandler implements PropertyChangeListener {
359 
360         public void propertyChange(PropertyChangeEvent evt) {
361             if (Boolean.TRUE.equals(evt.getNewValue())) {
362                 if (isLookChanged) {
363                     LOG.warn("You may have to restart Jukes to see the Look and Feel changes");
364                 }
365                 ExtUIManager.setLookConfigurations(configurations);
366                 final LookConfiguration config = createLookConfiguration();
367                 ExtUIManager.setDefaultLookConfiguration(config);
368                 final Preferences prefs = Application.getUserPreferences();
369                 if (config.getTheme() != null) {
370                    prefs.put("laf.themeName."+ config.getLookAndFeel().getName(), config.getTheme().getClass().getName());
371                 }
372                 
373             }
374         }
375     }
376 
377     // Renders instances of LookAndFeel displaying their name
378     private static class LooksListCellRenderer extends DefaultListCellRenderer {
379 
380         public Component getListCellRendererComponent(
381                 JList list, Object value, int index,
382                 boolean isSelected,
383                 boolean cellHasFocus) {
384             return super.getListCellRendererComponent(
385                 list,
386                 ((LookAndFeel) value).getName(),
387                 index, isSelected, cellHasFocus);
388         }
389     }
390     
391     // Renders instances of MetalTheme
392     private static class ThemesListCellRenderer extends DefaultListCellRenderer {
393 
394         public Component getListCellRendererComponent(
395                 JList list, Object value, int index,
396                 boolean isSelected,
397                 boolean cellHasFocus) {
398             return super.getListCellRendererComponent(
399                 list,
400                 (value instanceof PlasticTheme
401                     ? ((PlasticTheme) value).getName()
402                     : value),
403                 index, isSelected, cellHasFocus);
404         }
405     }
406     
407     /**
408     * An implementation of the <code>Preparable</code> interface 
409     * that computes a list of supported <code>LookAndFeel</code> instances.
410     */
411     public static class EagerInitializer implements Preparable {
412 
413         /**
414          * Ensures that the supported look and feels are computed and that 
415          * the look and feel preview panel has been loaded once.
416          */
417         public void prepare() {
418             Logger.getLogger("LookAndFeelPanel").info(
419                 "Computing supported look and feels...");
420             getSupportedLookAndFeelInstances();
421             Logger.getLogger("LookAndFeelPanel").info(
422                 "Preloading preview panel classes...");
423             new LookAndFeelPreviewPanel();
424         }
425     }
426 
427 }