/* @(#)LookAndFeelTools.java  2.6.0   20 June 2007
 *
 * Copyright 2007
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;
import java.util.*;

/**
 * <p>Class <CODE>LookAndFeelTools</CODE> contains several static methods
 * that assist in using the Java look and feel facilities.</p>
 *
 * <p>Class <CODE>LookAndFeelTools</CODE> cannot be instantiated.</p>
 *
 * <p>In 2.6.0, we needed to supply a workaround to a bug in Java 6.  If
 * you are interested, look up the source and see the comments in the
 * method <code>adjustAllDefaultFontSizes</code>.</p>
 * 
 * @author  Richard Rasala
 * @version 2.6.0
 * @since   2.3
 */
public class LookAndFeelTools implements JPTConstants
{
    
    /** Private constructor to prevent instantiation. */
    private LookAndFeelTools() {}
    
    
    /** The maximum font size adjustment shown in the look and feel dialog. */
    private static final int MAX_ADJUSTMENT = 20;
    
    
    /**
     * The net font size adjustment made after one or more calls of these
     * tools.
     */
    private static float netFontSizeAdjustment = 0;
    
    
    /**
     * The lower bound for the net font size adjustment.  This lower bound
     * prevents the font sizes from being set to very small or negative
     * values.
     */
    public static final float MINIMUM_NET_FONT_SIZE_ADJUSTMENT = -2;
    
    
    /**
     * <p>Displays a dialog that permits the user to select the look and feel
     * from among the look and feel options that are currently installed.</p>
     *
     * <p>In addition, permits the user to adjust the font size of each default
     * font by a fixed amount specified by a sequence of constants provided in
     * the dialog.</p>
     *
     * <p>This method must be called before any GUI objects are displayed on
     * screen.  Therefore, it is best to make the following code the first
     * line of the <CODE>main</CODE> method that launches the program:</p>
     *
     * <UL>
     *   <LI><CODE>LookAndFeelTools.showSelectLookAndFeelDialog();</CODE</LI>
     * </UL>
     */
    public static void showSelectLookAndFeelDialog() {
    
        UIManager.LookAndFeelInfo[] info = UIManager.getInstalledLookAndFeels();
        
        int length = info.length;
        
        String[] name = new String[length];
        String[] className = new String[length];
        
        for (int i = 0; i < length; i++) {
            name[i] = info[i].getName();
            className[i] = info[i].getClassName();
        }
        
        String selectNameString = "Select Look and Feel:";
        
        OptionsView selectNameView =
            new OptionsView(name, 0, new TableLayout(1, length));
        
        
        String fontSizeAdjustString = "Font Size Adjustment:";
        
        String[] fontSizeAdjustList =
            new String[MAX_ADJUSTMENT + 1];
        
        for (int i = 0; i <= MAX_ADJUSTMENT; i++)
            fontSizeAdjustList[i] = "" + i;
        
        DropdownView fontSizeAdjustView =
            new DropdownView(fontSizeAdjustList, "0", true);
        
        fontSizeAdjustView.setMaximumRowCount(MAX_ADJUSTMENT + 1);
        fontSizeAdjustView.setPreferredWidth(0, "00000000");
        
        TablePanel panel =
            new TablePanel(
                new Object[][] {
                    { selectNameString,     selectNameView     },
                    { fontSizeAdjustString, fontSizeAdjustView } },
                5, 10, WEST);
        
        
        String panelString = "Select Look and Feel";
        
        // show the dialog
        try {
            GeneralDialog.showOKCancelDialog(panel, panelString);
        }
        catch(Exception e) { return; } // return if dialog is cancelled
        
        
        // set the UI
        try {
            UIManager.setLookAndFeel(className[selectNameView.getSelectedIndex()]);
        }
        catch(Exception e) { return; } // return if error occurs
        
        
        // adjust the UI
        try {
            adjustAllDefaultFontSizes(fontSizeAdjustView.demandFloat());
        }
        catch (Exception e) { return; } // return if error occurs
    }
    
    
    /**
     * Returns the array of short names of the installed look and feel entities.
     */
    public static String[] getInstalledLookAndFeelNames() {
        UIManager.LookAndFeelInfo[] info = UIManager.getInstalledLookAndFeels();
        
        int length = info.length;
        
        String[] name = new String[length];
        
        for (int i = 0; i < length; i++) {
            name[i] = info[i].getName();
        }
        
        return name;
    }
    
    
    /**
     * Returns the array of class names of the installed look and feel entities.
     */
    public static String[] getInstalledLookAndFeelClassNames() {
        UIManager.LookAndFeelInfo[] info = UIManager.getInstalledLookAndFeels();
        
        int length = info.length;
        
        String[] className = new String[length];
        
        for (int i = 0; i < length; i++) {
            className[i] = info[i].getClassName();
        }
        
        return className;
    }
    
    
    /**
     * <p>Adjusts the font size of all default fonts by adding the given delta
     * to the current font size of each font.</p>
     *
     * <p>The method recognizes a default font by looking for the String "font"
     * in each key of a <CODE>UIDefaults</CODE> object.</p>
     *
     * <p>This method prevents the net font size adjustment obtained from one
     * or more calls to the tools in this package from becoming less than the
     * lower bound MINIMUM_NET_FONT_SIZE_ADJUSTMENT.</p>
     *
     * @param delta the incremental change in font sizes
     */
    public static void adjustAllDefaultFontSizes(float delta) {
        if ((netFontSizeAdjustment + delta) < MINIMUM_NET_FONT_SIZE_ADJUSTMENT)
            delta = MINIMUM_NET_FONT_SIZE_ADJUSTMENT - netFontSizeAdjustment;
        
        netFontSizeAdjustment += delta;
        
        // Up until Java 6, all keys associated with look and feel defaults
        // were of type String.
        //
        // In Java 6, there is a strange bug.
        //
        // For the default look and feel, there is one key that is of type
        // StringBuffer.  This is strange since normally a mutable object
        // should not be used as a hashtable key.
        //
        // This bug requires that the keys be extracted as an Object[] and
        // be examined key-by-key for String objects.
        
        UIDefaults defaults = UIManager.getLookAndFeelDefaults();
    
        Set keyset = defaults.keySet();
        
        Object[] keys = keyset.toArray();
        
        int length = keys.length;
        
        String key;
        Font   font;
        float  size;
        Object object;
        
        String fontWord = "font";
        
        for (int i = 0; i < length; i++) {
            if (keys[i] instanceof String) {
                key = (String) keys[i];
                
                if (key.indexOf(fontWord) >= 0) {
                    object = defaults.get(key);
                    
                    if (object instanceof Font) {
                        font = (Font) object;
                        size = font.getSize2D() + delta;
                        font = new FontUIResource(font.deriveFont(size));
                        
                        defaults.put(key, font);
                    }
                }
            }
        }
    }
    
    
    /**
     * <p>Sets the net font size adjustment to the look and feel fonts
     * to the given delta.</p>
     *
     * @param delta the absolute change in font sizes
     */
    public static void setNetFontSizeAdjustment(float delta) {
        adjustAllDefaultFontSizes(delta - netFontSizeAdjustment);
    }
    
    
    /**
     * <p>Returns the net font size adjustment to the look and feel fonts
     * after one or more calls to the tools in this package.</p>
     */
    public static float getNetFontSizeAdjustment() {
        return netFontSizeAdjustment;
    }
    
    
}
