/* MetalThemeEditor.java -- Edit themes using this application
   Copyright (C) 2006 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package gnu.classpath.examples.swing;

import gnu.javax.swing.plaf.metal.CustomizableTheme;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.metal.MetalLookAndFeel;

/**
 * This application serves two purposes: 1. demonstrate the color chooser
 * component, 2. make creating new Metal themes as easy as possible.
 *
 * @author Roman Kennke (kennke@aicas.com)
 */
public class MetalThemeEditor
  extends JPanel
{
  /**
   * An icon to display a chosen color in a button.
   */
  private class ColorIcon implements Icon
  {
    /**
     * The color to be shown on the icon.
     */
    Color color;

    /**
     * Creates a new ColorIcon.
     *
     * @param c the color to be displayed
     */
    ColorIcon(Color c)
    {
      color = c;
    }

    /**
     * Returns the icon height, which is 10.
     *
     * @return 10
     */
    public int getIconHeight()
    {
      return 10;
    }

    /**
     * Returns the icon width, which is 30.
     *
     * @return 30
     */
    public int getIconWidth()
    {
      return 30;
    }

    /**
     * Paints the icon.
     *
     * @param c the component to paint on
     * @param g the graphics to use
     * @param x the x location
     * @param y the y location
     */
    public void paintIcon(Component c, Graphics g, int x, int y)
    {
      g.setColor(color);
      g.fillRect(x, y, 30, 10);
    }
    
  }

  /**
   * Opens up a color chooser and lets the user select a color for the theme.
   */
  private class ChooseColorAction implements ActionListener
  {

    /**
     * The button that will get updated when a new color is selected.
     */
    private JButton button;

    /**
     * Specifies which color of the theme should be updated. See constants in
     * the MetalThemeEditor class.
     */
    private int colorType;

    /**
     * Creates a new ChooseColorAction. The specified button will have its
     * icon updated to the new color if appropriate.
     *
     * @param b the button to update
     * @param type the color type to update
     */
    ChooseColorAction(JButton b, int type)
    {
      button = b;
      colorType = type;
    }

    /**
     * Opens a color chooser and lets the user select a color.
     */
    public void actionPerformed(ActionEvent event)
    {
      Color c = JColorChooser.showDialog(button, "Choose a color",
                                         getColor(colorType));
      if (c != null)
        {
          setColor(colorType, c);
          button.setIcon(new ColorIcon(c));
        }
    }
  }

  /**
   * Denotes the primary1 color of the theme.
   */
  private static final int PRIMARY1 = 0;

  /**
   * Denotes the primary2 color of the theme.
   */
  private static final int PRIMARY2 = 1;

  /**
   * Denotes the primary3 color of the theme.
   */
  private static final int PRIMARY3 = 2;

  /**
   * Denotes the secondary1 color of the theme.
   */
  private static final int SECONDARY1 = 3;

  /**
   * Denotes the secondary2 color of the theme.
   */
  private static final int SECONDARY2 = 4;

  /**
   * Denotes the secondary3 color of the theme.
   */
  private static final int SECONDARY3 = 5;

  /**
   * The theme that is edited.
   */
  CustomizableTheme theme;

  /**
   * Creates a new instance of the MetalThemeEditor.
   */
  MetalThemeEditor()
  {
    theme = new CustomizableTheme();
    setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11));
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    add(createConfigurationPanel());
    add(Box.createVerticalStrut(17));
    add(createButtonPanel());
  }

  /**
   * Creates the main panel of the MetalThemeEditor. This is the upper
   * area where the colors can be selected.
   *
   * @return the main panel
   */
  private JPanel createConfigurationPanel()
  {
    JPanel p = new JPanel();
    p.setLayout(new GridBagLayout());
    GridBagConstraints c = new GridBagConstraints();
    c.weightx = 1;
    c.weighty = 0;
    c.fill = GridBagConstraints.HORIZONTAL;
    Insets labelInsets = new Insets(0, 0, 11, 6);
    Insets buttonInsets = new Insets(0, 0, 11, 0);

    // Primary 1
    JLabel primary1Label = new JLabel("Primary 1:");
    c.gridx = 0;
    c.gridy = 0;
    c.insets = labelInsets;
    p.add(primary1Label, c);

    Icon p1Icon = new ColorIcon(theme.getPrimary1());
    JButton primary1Button = new JButton(p1Icon);
    primary1Button.addActionListener(new ChooseColorAction(primary1Button,
                                                           PRIMARY1));
    //c.weightx = 0;
    c.gridx = 1;
    c.insets = buttonInsets;
    p.add(primary1Button, c);
    primary1Label.setLabelFor(primary1Button);

    // Primary 2
    JLabel primary2Label = new JLabel("Primary 2:");
    c.gridx = 0;
    c.gridy = 1;
    c.insets = labelInsets;
    p.add(primary2Label, c);

    Icon p2Icon = new ColorIcon(theme.getPrimary2());
    JButton primary2Button = new JButton(p2Icon);
    primary2Button.addActionListener(new ChooseColorAction(primary2Button,
                                                           PRIMARY2));
    c.gridx = 1;
    c.insets = buttonInsets;
    p.add(primary2Button, c);
    primary2Label.setLabelFor(primary2Button);

    // Primary 3
    JLabel primary3Label = new JLabel("Primary 3:");
    c.gridx = 0;
    c.gridy = 2;
    c.insets = labelInsets;
    p.add(primary3Label, c);

    Icon p3Icon = new ColorIcon(theme.getPrimary3());
    JButton primary3Button = new JButton(p3Icon);
    primary3Button.addActionListener(new ChooseColorAction(primary3Button,
                                                           PRIMARY3));
    c.gridx = 1;
    c.insets = buttonInsets;
    p.add(primary3Button, c);
    primary3Label.setLabelFor(primary3Button);

    // Secondary 1
    JLabel secondary1Label = new JLabel("Secondary 1:");
    c.gridx = 0;
    c.gridy = 3;
    c.insets = labelInsets;
    p.add(secondary1Label, c);

    Icon s1Icon = new ColorIcon(theme.getSecondary1());
    JButton secondary1Button = new JButton(s1Icon);
    secondary1Button.addActionListener(new ChooseColorAction(secondary1Button,
                                                             SECONDARY1));
    c.gridx = 1;
    c.insets = buttonInsets;
    p.add(secondary1Button, c);
    secondary1Label.setLabelFor(secondary1Button);

    // Secondary 2
    JLabel secondary2Label = new JLabel("Secondary 2:");
    c.gridx = 0;
    c.gridy = 4;
    c.insets = labelInsets;
    p.add(secondary2Label, c);

    Icon s2Icon = new ColorIcon(theme.getSecondary2());
    JButton secondary2Button = new JButton(s2Icon);
    secondary2Button.addActionListener(new ChooseColorAction(secondary2Button,
                                                             SECONDARY2));
    c.gridx = 1;
    c.insets = buttonInsets;
    p.add(secondary2Button, c);
    secondary2Label.setLabelFor(secondary2Button);

    // Secondary 3
    JLabel secondary3Label = new JLabel("Secondary 3:");
    c.gridx = 0;
    c.gridy = 5;
    c.insets = labelInsets;
    p.add(secondary3Label, c);

    Icon s3Icon = new ColorIcon(theme.getSecondary3());
    JButton secondary3Button = new JButton(s3Icon);
    secondary3Button.addActionListener(new ChooseColorAction(secondary3Button,
                                                             SECONDARY3));
    c.gridx = 1;
    c.insets = buttonInsets;
    p.add(secondary3Button, c);
    secondary3Label.setLabelFor(secondary3Button);

    return p;
  }

  /**
   * Creates the button panel at the bottom of the MetalThemeEditor.
   *
   * @return the button panel
   */
  private JPanel createButtonPanel()
  {
    JPanel p = new JPanel();
    p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
    p.add(Box.createHorizontalGlue());

    JButton applyButton = new JButton("Apply");
    applyButton.addActionListener
    (new ActionListener()
     {
       public void actionPerformed(ActionEvent ev)
       {
         try
           {
             CustomizableTheme copy = (CustomizableTheme) theme.clone();
             MetalLookAndFeel.setCurrentTheme(copy);
             UIManager.setLookAndFeel(new MetalLookAndFeel());
             Window w = SwingUtilities.getWindowAncestor(MetalThemeEditor.this);
             SwingUtilities.updateComponentTreeUI(w);
           }
         catch (Exception ex)
           {
             ex.printStackTrace();
           }
       }
     });
    p.add(applyButton);

    p.add(Box.createHorizontalStrut(5));

    JButton exportButton = new JButton("Export as Java File");
    exportButton.addActionListener
    (new ActionListener()
     {
       public void actionPerformed(ActionEvent ev)
       {
         export();
       }
     });
    p.add(exportButton);

    return p;
  }

  /**
   * Exports the current theme as Java source file. This will prompt the user
   * to choose a filename.
   */
  void export()
  {
    JFileChooser chooser = new JFileChooser();
    int confirm = chooser.showSaveDialog(this);
    if (confirm == JFileChooser.APPROVE_OPTION)
      exportToFile(chooser.getSelectedFile());
  }

  /**
   * Writes out the current configured Metal theme as Java source file.
   *
   * @param file the file to write into
   */
  void exportToFile(File file)
  {
    String fileName = file.getName();
    if (! fileName.endsWith(".java"))
      {
        JOptionPane.showMessageDialog(this,
                                 "Filename does not denote a Java source file",
                                 "Invalid filename",
                                 JOptionPane.ERROR_MESSAGE);
        return;
      }

    String className = fileName.substring(0, fileName.length() - 5);
    Color p1 = theme.getPrimary1();
    Color p2 = theme.getPrimary2();
    Color p3 = theme.getPrimary3();
    Color s1 = theme.getSecondary1();
    Color s2 = theme.getSecondary2();
    Color s3 = theme.getSecondary3();

    try
      {
        FileOutputStream out = new FileOutputStream(file);
        Writer writer = new OutputStreamWriter(out);
        writer.write("import javax.swing.plaf.ColorUIResource;\n");
        writer.write("import javax.swing.plaf.metal.DefaultMetalTheme;\n");
        writer.write("public class " + className + " extends DefaultMetalTheme\n");
        writer.write("{\n");
        writer.write("  protected ColorUIResource getPrimary1()\n");
        writer.write("  {\n");
        writer.write("    return new ColorUIResource(" + p1.getRGB() + ");\n");
        writer.write("  }\n");
        writer.write("  protected ColorUIResource getPrimary2()\n");
        writer.write("  {\n");
        writer.write("    return new ColorUIResource(" + p2.getRGB() + ");\n");
        writer.write("  }\n");
        writer.write("  protected ColorUIResource getPrimary3()\n");
        writer.write("  {\n");
        writer.write("    return new ColorUIResource(" + p3.getRGB() + ");\n");
        writer.write("  }\n");
        writer.write("  protected ColorUIResource getSecondary1()\n");
        writer.write("  {\n");
        writer.write("    return new ColorUIResource(" + s1.getRGB() + ");\n");
        writer.write("  }\n");
        writer.write("  protected ColorUIResource getSecondary2()\n");
        writer.write("  {\n");
        writer.write("    return new ColorUIResource(" + s2.getRGB() + ");\n");
        writer.write("  }\n");
        writer.write("  protected ColorUIResource getSecondary3()\n");
        writer.write("  {\n");
        writer.write("    return new ColorUIResource(" + s3.getRGB() + ");\n");
        writer.write("  }\n");
        writer.write("}\n");
        writer.close();
        out.close();
      }
    catch (FileNotFoundException ex)
      {
        ex.printStackTrace();
      }
    catch (IOException ex)
      {
        ex.printStackTrace();
      }
    
  }

  /**
   * Returns the color of the theme with the specified type. For the possible
   * types see the constants of this class.
   *
   * @param colorType the color type to fetch from the theme
   *
   * @return the current color of the specified type
   *
   * @throws IllegalArgumentException for illegal color types
   */
  Color getColor(int colorType)
  {
    Color color = null;
    switch (colorType)
    {
    case PRIMARY1:
      color = theme.getPrimary1();
      break;
    case PRIMARY2:
      color = theme.getPrimary2();
      break;
    case PRIMARY3:
      color = theme.getPrimary3();
      break;
    case SECONDARY1:
      color = theme.getSecondary1();
      break;
    case SECONDARY2:
      color = theme.getSecondary2();
      break;
    case SECONDARY3:
      color = theme.getSecondary3();
      break;
    default:
      throw new IllegalArgumentException("Unknown color type: " + colorType);
    }
    return color;
  }

  /**
   * Sets the color of the specified type in the current theme.
   *
   * @param colorType the color type
   * @param color the color to set
   *
   * @throws IllegalArgumentException for illegal color types
   */
  void setColor(int colorType, Color color)
  {
    switch (colorType)
    {
      case PRIMARY1:
        theme.setPrimary1(color);
        break;
      case PRIMARY2:
        theme.setPrimary2(color);
        break;
      case PRIMARY3:
        theme.setPrimary3(color);
        break;
      case SECONDARY1:
        theme.setSecondary1(color);
        break;
      case SECONDARY2:
        theme.setSecondary2(color);
        break;
      case SECONDARY3:
        theme.setSecondary3(color);
        break;
      default:
        throw new IllegalArgumentException("Illegal color type: " + colorType);
    }
  }

  /**
   * The entry point to the application.
   *
   * @param args ignored
   */
  public static void main(String[] args)
  {
    JFrame f = new JFrame("MetalThemeEditor");
    f.setContentPane(new MetalThemeEditor());
    f.pack();
    f.setVisible(true);
  }

  /**
   * Returns a DemoFactory that creates a MetalThemeEditor.
   *
   * @return a DemoFactory that creates a MetalThemeEditor
   */
  public static DemoFactory createDemoFactory()
  {
    return new DemoFactory()
    {
      public JComponent createDemo()
      {
        return new MetalThemeEditor();
      }
    };
  }
}
