/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: ColorPatternPanel.java
*
* Copyright (c) 2004, Static Free Software. All rights reserved.
*
* Electric(tm) 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 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.sun.electric.tool.user.dialogs;

import com.sun.electric.database.geometry.EGraphics;
import com.sun.electric.database.geometry.EGraphics.Outline;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.user.Resources;
import com.sun.electric.tool.user.User;
import com.sun.electric.util.TextUtils;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/**
* A Panel to display color and pattern information.
* Used in the "Layers" tab of the "Edit Options" dialog.
* Used in the "Artwork Look" dialog.
*/
public class ColorPatternPanel extends JPanel
{
	/**
	 * Class to define the information on a color pattern panel.
	 */
	public static class Info
	{
		public EGraphics graphics;
		public int [] pattern;
		public boolean useStippleDisplay;
		public Outline outlinePatternDisplay;
		public boolean useStipplePrinter;
		public int transparentLayer;
		public int red, green, blue;
        public User.ColorPrefType theColor;  // theColor to remember the factory color for special layers
		public double opacity;
		public boolean justColor;

		/**
		 * Constructor to load a color described by an EGraphics object.
		 */
		public Info(EGraphics graphics)
		{
			this.graphics = graphics;
			this.pattern = new int[16];
			int [] pattern = graphics.getPattern();
			for(int i=0; i<16; i++) this.pattern[i] = pattern[i];
			useStippleDisplay = graphics.isPatternedOnDisplay();
			outlinePatternDisplay = graphics.getOutlined();
			useStipplePrinter = graphics.isPatternedOnPrinter();
			transparentLayer = graphics.getTransparentLayer();
			int color = graphics.getColor().getRGB();
			red = (color >> 16) & 0xFF;
			green = (color >> 8) & 0xFF;
			blue = color & 0xFF;
			opacity = graphics.getOpacity();
			justColor = false; theColor = null;
		}

		/**
		 * Constructor for class to load a pure color.
		 * Used for special colors (like background, etc.)
		 */
		public Info(User.ColorPrefType colorPref)
		{
            int color = User.getColor(colorPref);
            red = (color >> 16) & 0xFF;
			green = (color >> 8) & 0xFF;
			blue = color & 0xFF;
            theColor = colorPref;
            justColor = true;
		}

		/**
		 * Method to update the EGraphics object that is being displayed in this dialog panel.
		 * @return true if the EGraphics object changed.
		 */
		public EGraphics updateGraphics(EGraphics graphics)
		{
			if (justColor) return graphics;

			graphics = graphics.withPattern(pattern);
			graphics = graphics.withPatternedOnDisplay(useStippleDisplay);
			graphics = graphics.withOutlined(outlinePatternDisplay);
			graphics = graphics.withPatternedOnPrinter(useStipplePrinter);
			graphics = graphics.withColor(new Color(red, green, blue));
			graphics = graphics.withOpacity(opacity);
			graphics = graphics.withTransparentLayer(transparentLayer);
			return graphics;
		}

	}

	private PatternView patternView;
	private PatternChoices patternIcon;
	private Info currentLI;
	private boolean dataChanging = false;
	private boolean showPrinter;
	private Color [] colorMap;
	private JColorChooser colorChooser;
	private MyPreviewPanel colorPreviewPanel;
	private boolean warnedOfTransparentLayerSharing;
	private String otherTransparentLayers;
    private Map<Outline,ImageIcon> outlineImageIcons = new HashMap<Outline,ImageIcon>();

	/**
	 * Create a Panel for editing color and pattern information.
	 */
	public ColorPatternPanel(boolean showPrinter)
	{
		initComponents();

		this.showPrinter = showPrinter;
		warnedOfTransparentLayerSharing = false;

		useStipplePatternDisplay.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt) { layerInfoChanged(); }
		});
		for(Outline o : Outline.getOutlines())
		{
            ImageIcon imageIcon = getSample(o);
            outlineImageIcons.put(o, imageIcon);
			outlinePattern.addItem(imageIcon);
		}
		outlinePattern.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt) { layerInfoChanged(); }
		});
		transparentLayer.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt) { transparentLayerChanged(); }
		});
		transparentLayer.addItem("NOT TRANSPARENT");
		int [] transLayers = EGraphics.getTransparentColorIndices();
		for(int i=0; i<transLayers.length; i++)
			transparentLayer.addItem(EGraphics.getColorIndexName(transLayers[i]));

		patternView = new PatternView(currentLI, useStipplePatternDisplay, outlinePattern);
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = 0;       gbc.gridy = 1;
		gbc.gridwidth = 3;   gbc.gridheight = 1;
		gbc.insets = new Insets(0, 4, 4, 4);
		pattern.add(patternView, gbc);

		patternIcon = new PatternChoices();
		gbc = new GridBagConstraints();
		gbc.gridx = 0;   gbc.gridy = 3;
		gbc.gridwidth = 3;   gbc.gridheight = 1;
		gbc.insets = new Insets(0, 4, 2, 4);
		pattern.add(patternIcon, gbc);

		colorChooser = new JColorChooser();
		gbc = new GridBagConstraints();
		gbc.gridx = 0;   gbc.gridy = 1;
		gbc.gridwidth = 2;
		color.add(colorChooser, gbc);
		colorChooser.getSelectionModel().addChangeListener(new ChangeListener()
		{
			public void stateChanged(ChangeEvent e) { colorChanged(); }
		});
		colorChooser.setPreviewPanel(new JPanel());

		colorPreviewPanel = new MyPreviewPanel(this);
		gbc = new GridBagConstraints();
		gbc.gridx = 0;   gbc.gridy = 2;
		gbc.gridwidth = 2;
		gbc.insets = new Insets(4, 4, 4, 4);
		color.add(colorPreviewPanel, gbc);

		if (showPrinter)
		{
			useStipplePatternPrinter.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent evt) { layerInfoChanged(); }
			});
			opacity.getDocument().addDocumentListener(new LayerColorDocumentListener());
		} else
		{
			pattern.remove(useStipplePatternPrinter);
			pattern.remove(jLabel1);
			pattern.remove(jLabel2);
			pattern.remove(opacityLabel);
			pattern.remove(opacity);
		}
	}

	/**
	 * Class to provide an alternative preview panel for JColorChooser.
	 */
	private class MyPreviewPanel extends JButton
	{
		private static final int XSIZE = 288;
		private static final int YSIZE = 48;
		private static final int BORDER = 10;
		private ColorPatternPanel dia;
		Color curColor = Color.BLACK;

		public MyPreviewPanel(ColorPatternPanel dia)
		{
			this.dia = dia;
			setPreferredSize(new Dimension(XSIZE+BORDER*2, YSIZE+BORDER*2));
		}

		public void setPreviewColor(Color color)
		{
			curColor = color;
		}

		public void paint(Graphics g)
		{
			// clear background
			g.setColor(new Color(User.getColor(User.ColorPrefType.BACKGROUND)));
			g.fillRect(0, 0, getWidth(), getHeight());
			if (currentLI == null) return;

			if (dia.useStipplePatternDisplay.isSelected())
			{
				// stippled: construct an image
				BufferedImage im = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB);
				for(int y=0; y<16; y++)
				{
					int line = currentLI.pattern[y];
					for(int x=0; x<16; x++)
					{
						if ((line & (1<<(15-x))) != 0) im.setRGB(x, y, curColor.getRGB()); else
							im.setRGB(x, y, User.getColor(User.ColorPrefType.BACKGROUND));
					}
				}

				// draw the image to fill the preview
				for(int y=0; y<YSIZE; y+=16)
					for(int x=0; x<XSIZE; x+=16)
						g.drawImage(im, x+BORDER, y+BORDER, null, null);

				// draw an outline if requested
				List<Outline> outlines = Outline.getOutlines();
				Outline o = outlines.get(dia.outlinePattern.getSelectedIndex());
				if (o != Outline.NOPAT)
				{
					g.setColor(curColor);
					for(int t=0; t<o.getThickness(); t++)
					{
						if (o.isSolidPattern())
						{
							g.drawLine(BORDER+t, BORDER+t, XSIZE+BORDER-1-t, BORDER+t);
							g.drawLine(XSIZE+BORDER-1-t, BORDER+t, XSIZE+BORDER-1-t, YSIZE+BORDER-1-t);
							g.drawLine(XSIZE+BORDER-1-t, YSIZE+BORDER-1-t, BORDER+t, YSIZE+BORDER-1-t);
							g.drawLine(BORDER+t, YSIZE+BORDER-1-t, BORDER+t, BORDER+t);
						} else
						{
							int pattern = o.getPattern();
							int len = o.getLen();

							// draw the top and bottom lines
							int patPos = 0;
							for(int x=0; x<XSIZE; x++)
							{
								if ((pattern & (1<<patPos)) != 0)
								{
									g.fillRect(BORDER+x, BORDER+t, 1, 1);
									g.fillRect(BORDER+x, YSIZE+BORDER-1-t, 1, 1);
								}
								patPos++;
								if (patPos >= len) patPos = 0;
							}

							// draw the left and right lines
							patPos = 0;
							for(int y=0; y<YSIZE; y++)
							{
								if ((pattern & (1<<patPos)) != 0)
								{
									g.fillRect(BORDER+t, BORDER+y, 1, 1);
									g.fillRect(XSIZE+BORDER-1-t, BORDER+y, 1, 1);
								}
								patPos++;
								if (patPos >= len) patPos = 0;
							}
						}
					}
				}
			} else
			{
				// not stippled, just fill it
				g.setColor(curColor);
				g.fillRect(BORDER, BORDER, XSIZE, YSIZE);
			}
	   }
   }

	/**
	 * Method to update the panel to reflect the given color map.
	 * @param map the color map to be used.
	 */
	public void setColorMap(Color [] map)
	{
		colorMap = map;
		int curTrans = transparentLayer.getSelectedIndex();
		int [] transLayers = EGraphics.getTransparentColorIndices();
		transparentLayer.removeAllItems();
		transparentLayer.addItem("NOT TRANSPARENT");
		int maxTrans = Math.min(transLayers.length, map.length);
        if (maxTrans > 0)  // zero for generic at least
        {
            for(int i=0; i<maxTrans; i++)
                transparentLayer.addItem(EGraphics.getColorIndexName(transLayers[i]));
            transparentLayer.setSelectedIndex(curTrans);
        }
	}

	public void setOtherTransparentLayerNames(String names) { otherTransparentLayers = names; }

	/**
	 * Method to update the panel to reflect the given Info.
	 * @param li the Info structure with data for this panel.
	 * The Info structure contains color, texture, and other appearance-related factors for drawing.
	 */
	public void setColorPattern(Info li)
	{
		currentLI = li;
		patternView.setLayerInfo(li);
		if (li == null)
		{
			useStipplePatternDisplay.setEnabled(false);
			outlinePattern.setEnabled(false);
			useStipplePatternPrinter.setEnabled(false);
			transparentLayer.setEnabled(false);
			colorChooser.setEnabled(false);
			return;
		}
		colorChooser.setEnabled(true);
		if (li.justColor)
		{
			useStipplePatternDisplay.setEnabled(false);
			outlinePattern.setEnabled(false);
			useStipplePatternPrinter.setEnabled(false);
			transparentLayer.setEnabled(false);
		} else
		{
			useStipplePatternDisplay.setEnabled(true);
			outlinePattern.setEnabled(true);
			useStipplePatternPrinter.setEnabled(true);
			transparentLayer.setEnabled(true);
		}
		dataChanging = true;
		useStipplePatternDisplay.setSelected(li.useStippleDisplay);
		if (li.outlinePatternDisplay != null) // special cases such text don't have outlinePattern
            outlinePattern.setSelectedItem(outlineImageIcons.get(li.outlinePatternDisplay));
		outlinePattern.setEnabled(li.useStippleDisplay);
		if (showPrinter)
		{
			useStipplePatternPrinter.setSelected(li.useStipplePrinter);
			opacity.setText(TextUtils.formatDouble(li.opacity));
		}
		if (li.transparentLayer < transparentLayer.getItemCount())
			transparentLayer.setSelectedIndex(li.transparentLayer);
		Color initialColor = new Color(li.red, li.green, li.blue);
		colorChooser.setColor(initialColor);
		colorPreviewPanel.setPreviewColor(initialColor);
		patternView.repaint();
		colorPreviewPanel.repaint();
		dataChanging = false;
	}

	/**
	 * Method called when the color picker selects a new color.
	 */
	private void colorChanged()
	{
		if (dataChanging) return;
		if (currentLI == null) return;
		Color col = colorChooser.getColor();
		currentLI.red = col.getRed();
		currentLI.green = col.getGreen();
		currentLI.blue = col.getBlue();
		layerInfoChanged();
		colorPreviewPanel.setPreviewColor(col);

		// if there are other transparent layers, warn (first time only)
		if (otherTransparentLayers != null)
		{
			if (!warnedOfTransparentLayerSharing)
				Job.getUserInterface().showInformationMessage(null, "WARNING: changing this color also affects " +
					otherTransparentLayers + " because they share the same transparent layer",
					"Change to Transparent Layer Color");
			warnedOfTransparentLayerSharing = true;
		}
	}

	private void transparentLayerChanged()
	{
		if (currentLI == null) return;
		currentLI.transparentLayer = transparentLayer.getSelectedIndex();
		if (currentLI.transparentLayer > 0 && colorMap != null)
		{
			if (currentLI.transparentLayer > colorMap.length)
			{
				transparentLayer.setSelectedIndex(0);
				return;
			}
			currentLI.red = colorMap[currentLI.transparentLayer-1].getRed();
			currentLI.green = colorMap[currentLI.transparentLayer-1].getGreen();
			currentLI.blue = colorMap[currentLI.transparentLayer-1].getBlue();
			layerInfoChanged();
		}
	}

	private void layerInfoChanged()
	{
		if (dataChanging) return;
		if (currentLI == null) return;
		currentLI.useStippleDisplay = useStipplePatternDisplay.isSelected();
		List<Outline> outlines = Outline.getOutlines();
		currentLI.outlinePatternDisplay = outlines.get(outlinePattern.getSelectedIndex());
		outlinePattern.setEnabled(currentLI.useStippleDisplay);
		if (showPrinter)
		{
			currentLI.useStipplePrinter = useStipplePatternPrinter.isSelected();
		}
		currentLI.transparentLayer = transparentLayer.getSelectedIndex();
//		boolean colorsEnabled = currentLI.transparentLayer == 0;
		currentLI.opacity = TextUtils.atof(opacity.getText());
		Color newColor = new Color(currentLI.red, currentLI.green, currentLI.blue);
		colorChooser.setColor(newColor);
		if (currentLI.transparentLayer != 0)
			colorMap[currentLI.transparentLayer-1] = newColor;
		colorPreviewPanel.repaint();
	}

	/**
	 * Class to handle special changes to color information.
	 */
	private class LayerColorDocumentListener implements DocumentListener
	{
		LayerColorDocumentListener() {}

		public void changedUpdate(DocumentEvent e) { layerInfoChanged(); }
		public void insertUpdate(DocumentEvent e) { layerInfoChanged(); }
		public void removeUpdate(DocumentEvent e) { layerInfoChanged(); }
	}

	private class PatternView extends JPanel
		implements MouseMotionListener, MouseListener
	{
		private static final int PATSIZE = 13;

		private boolean newState;
		private JCheckBox stipple;
		private JComboBox outline;
		private Info lInfo;

		PatternView(Info lInfo, JCheckBox stipple, JComboBox outline)
		{
			this.lInfo = lInfo;
			this.stipple = stipple;
			this.outline = outline;
			addMouseListener(this);
			addMouseMotionListener(this);
			int totSize = PATSIZE*16+1;
			setMaximumSize(new Dimension(totSize, totSize));
			setMinimumSize(new Dimension(totSize, totSize));
			setPreferredSize(new Dimension(totSize, totSize));
		}

		public void setLayerInfo(Info lInfo) { this.lInfo = lInfo; }

		/**
		 * Method to repaint this PatternView.
		 */
		public void paint(Graphics g)
		{
			Dimension dim = getSize();
			g.setColor(Color.WHITE);
			g.fillRect(0, 0, dim.width, dim.height);
			g.setColor(Color.GRAY);
			int upper = PATSIZE * 16;
			for(int i=0; i<=upper; i += PATSIZE)
			{
				g.drawLine(i, 0, i, upper);
				g.drawLine(0, i, upper, i);
			}

			g.setColor(Color.BLACK);
			if (lInfo == null || lInfo.justColor) return;
			for(int y=0; y<16; y++)
			{
				int bits = lInfo.pattern[y];
				for(int x=0; x<16; x++)
				{
					if ((bits & (1<<(15-x))) != 0)
					{
						g.fillRect(x*PATSIZE+1, y*PATSIZE+1, PATSIZE-1, PATSIZE-1);
					}
				}
			}
		}

		// the MouseListener events
		public void mousePressed(MouseEvent evt)
		{
			if (lInfo == null || lInfo.justColor) return;
			int xIndex = evt.getX() / PATSIZE;
			int yIndex = evt.getY() / PATSIZE;
			int curWord = lInfo.pattern[yIndex];
			newState = (curWord & (1<<(15-xIndex))) == 0;
			mouseDragged(evt);
		}
		public void mouseReleased(MouseEvent evt) {}
		public void mouseClicked(MouseEvent evt) {}
		public void mouseEntered(MouseEvent evt) {}
		public void mouseExited(MouseEvent evt) {}

		// the MouseMotionListener events
		public void mouseMoved(MouseEvent evt) {}
		public void mouseDragged(MouseEvent evt)
		{
			if (lInfo == null || lInfo.justColor) return;
			int xIndex = evt.getX() / PATSIZE;
			int yIndex = evt.getY() / PATSIZE;
			if (xIndex < 0 || yIndex < 0 || xIndex >= 16 || yIndex >= 16) return;
			int curWord = lInfo.pattern[yIndex];
			if ((curWord & (1<<(15-xIndex))) != 0)
			{
				if (newState) return;
				curWord &= ~(1<<(15-xIndex));
			} else
			{
				if (!newState) return;
				curWord |= 1<<(15-xIndex);
			}
			lInfo.pattern[yIndex] = curWord;

			// fake a check in the stipple use
			stipple.setSelected(true);
			outline.setEnabled(true);
			lInfo.useStippleDisplay = true;
			repaint();
			colorPreviewPanel.repaint();
		}
	}

	private static final int [] preDefinedPatterns =
	{
		0x8888,  // X   X   X   X
		0x4444,  //  X   X   X   X
		0x2222,  //   X   X   X   X
		0x1111,  //    X   X   X   X
		0x8888,  // X   X   X   X
		0x4444,  //  X   X   X   X
		0x2222,  //   X   X   X   X
		0x1111,  //    X   X   X   X
		0x8888,  // X   X   X   X
		0x4444,  //  X   X   X   X
		0x2222,  //   X   X   X   X
		0x1111,  //    X   X   X   X
		0x8888,  // X   X   X   X
		0x4444,  //  X   X   X   X
		0x2222,  //   X   X   X   X
		0x1111,  //    X   X   X   X

		0x8888,  // X   X   X   X
		0x1111,  //    X   X   X   X
		0x2222,  //   X   X   X   X
		0x4444,  //  X   X   X   X
		0x8888,  // X   X   X   X
		0x1111,  //    X   X   X   X
		0x2222,  //   X   X   X   X
		0x4444,  //  X   X   X   X
		0x8888,  // X   X   X   X
		0x1111,  //    X   X   X   X
		0x2222,  //   X   X   X   X
		0x4444,  //  X   X   X   X
		0x8888,  // X   X   X   X
		0x1111,  //    X   X   X   X
		0x2222,  //   X   X   X   X
		0x4444,  //  X   X   X   X

		0xCCCC,  // XX  XX  XX  XX
		0xCCCC,  // XX  XX  XX  XX
		0x3333,  //   XX  XX  XX  XX
		0x3333,  //   XX  XX  XX  XX
		0xCCCC,  // XX  XX  XX  XX
		0xCCCC,  // XX  XX  XX  XX
		0x3333,  //   XX  XX  XX  XX
		0x3333,  //   XX  XX  XX  XX
		0xCCCC,  // XX  XX  XX  XX
		0xCCCC,  // XX  XX  XX  XX
		0x3333,  //   XX  XX  XX  XX
		0x3333,  //   XX  XX  XX  XX
		0xCCCC,  // XX  XX  XX  XX
		0xCCCC,  // XX  XX  XX  XX
		0x3333,  //   XX  XX  XX  XX
		0x3333,  //   XX  XX  XX  XX

		0xFFFF,  // XXXXXXXXXXXXXXXX
		0x0000,  //
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0x0000,  //
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0x0000,  //
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0x0000,  //
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0x0000,  //
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0x0000,  //
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0x0000,  //
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0x0000,  //

		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X
		0xAAAA,  // X X X X X X X X

		0x6060,  //  XX      XX
		0x9090,  // X  X    X  X
		0x9090,  // X  X    X  X
		0x6060,  //  XX      XX
		0x0606,  //      XX      XX
		0x0909,  //     X  X    X  X
		0x0909,  //     X  X    X  X
		0x0606,  //      XX      XX
		0x6060,  //  XX      XX
		0x9090,  // X  X    X  X
		0x9090,  // X  X    X  X
		0x6060,  //  XX      XX
		0x0606,  //      XX      XX
		0x0909,  //     X  X    X  X
		0x0909,  //     X  X    X  X
		0x0606,  //      XX      XX

		0x2222,  //   X   X   X   X
		0x0000,  //
		0x8888,  // X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x0000,  //
		0x8888,  // X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x0000,  //
		0x8888,  // X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x0000,  //
		0x8888,  // X   X   X   X
		0x0000,  //

		0x4444,  //  X   X   X   X
		0x1111,  //    X   X   X   X
		0x4444,  //  X   X   X   X
		0x1111,  //    X   X   X   X
		0x4444,  //  X   X   X   X
		0x1111,  //    X   X   X   X
		0x4444,  //  X   X   X   X
		0x1111,  //    X   X   X   X
		0x4444,  //  X   X   X   X
		0x1111,  //    X   X   X   X
		0x4444,  //  X   X   X   X
		0x1111,  //    X   X   X   X
		0x4444,  //  X   X   X   X
		0x1111,  //    X   X   X   X
		0x4444,  //  X   X   X   X
		0x1111,  //    X   X   X   X

		0x1010,  //    X       X
		0x2020,  //   X       X
		0x4040,  //  X       X
		0x8080,  // X       X
		0x0101,  //        X       X
		0x0202,  //       X       X
		0x0404,  //      X       X
		0x0808,  //     X       X
		0x1010,  //    X       X
		0x2020,  //   X       X
		0x4040,  //  X       X
		0x8080,  // X       X
		0x0101,  //        X       X
		0x0202,  //       X       X
		0x0404,  //      X       X
		0x0808,  //     X       X

		0x0808,  //     X       X
		0x0404,  //      X       X
		0x0202,  //       X       X
		0x0101,  //        X       X
		0x8080,  // X       X
		0x4040,  //  X       X
		0x2020,  //   X       X
		0x1010,  //    X       X
		0x0808,  //     X       X
		0x0404,  //      X       X
		0x0202,  //       X       X
		0x0101,  //        X       X
		0x8080,  // X       X
		0x4040,  //  X       X
		0x2020,  //   X       X
		0x1010,  //    X       X

		0x4040,  //  X       X
		0x8080,  // X       X
		0x0101,  //        X       X
		0x0202,  //       X       X
		0x0101,  //        X       X
		0x8080,  // X       X
		0x4040,  //  X       X
		0x2020,  //   X       X
		0x4040,  //  X       X
		0x8080,  // X       X
		0x0101,  //        X       X
		0x0202,  //       X       X
		0x0101,  //        X       X
		0x8080,  // X       X
		0x4040,  //  X       X
		0x2020,  //   X       X

		0x2020,  //   X       X
		0x0000,  //
		0x8080,  // X       X
		0x0000,  //
		0x0202,  //       X       X
		0x0000,  //
		0x0808,  //     X       X
		0x0000,  //
		0x2020,  //   X       X
		0x0000,  //
		0x8080,  // X       X
		0x0000,  //
		0x0202,  //       X       X
		0x0000,  //
		0x0808,  //     X       X
		0x0000,  //

		0x0808,  //     X       X
		0x0000,  //
		0x0202,  //       X       X
		0x0000,  //
		0x8080,  // X       X
		0x0000,  //
		0x2020,  //   X       X
		0x0000,  //
		0x0808,  //     X       X
		0x0000,  //
		0x0202,  //       X       X
		0x0000,  //
		0x8080,  // X       X
		0x0000,  //
		0x2020,  //   X       X
		0x0000,  //

		0x0000,  //
		0x0303,  //       XX      XX
		0x4848,  //  X  X    X  X
		0x0303,  //       XX      XX
		0x0000,  //
		0x3030,  //   XX      XX
		0x8484,  // X    X  X    X
		0x3030,  //   XX      XX
		0x0000,  //
		0x0303,  //       XX      XX
		0x4848,  //  X  X    X  X
		0x0303,  //       XX      XX
		0x0000,  //
		0x3030,  //   XX      XX
		0x8484,  // X    X  X    X
		0x3030,  //   XX      XX

		0x1C1C,  //    XXX     XXX
		0x3E3E,  //   XXXXX   XXXXX
		0x3636,  //   XX XX   XX XX
		0x3E3E,  //   XXXXX   XXXXX
		0x1C1C,  //    XXX     XXX
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x1C1C,  //    XXX     XXX
		0x3E3E,  //   XXXXX   XXXXX
		0x3636,  //   XX XX   XX XX
		0x3E3E,  //   XXXXX   XXXXX
		0x1C1C,  //    XXX     XXX
		0x0000,  //
		0x0000,  //
		0x0000,  //

		0x0000,  //
		0xCCCC,  // XX  XX  XX  XX
		0x0000,  //
		0xCCCC,  // XX  XX  XX  XX
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0xCCCC,  // XX  XX  XX  XX
		0x0000,  //
		0xCCCC,  // XX  XX  XX  XX
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //

		0x0000,  //
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x8888,  // X   X   X   X
		0x0000,  //
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x8888,  // X   X   X   X
		0x0000,  //
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x8888,  // X   X   X   X
		0x0000,  //
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x8888,  // X   X   X   X

		0x0000,  //
		0x0000,  //
		0x1111,  //    X   X   X   X
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x1111,  //    X   X   X   X
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x1111,  //    X   X   X   X
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x1111,  //    X   X   X   X
		0x0000,  //

		0x0000,  //
		0x2222,  //   X   X   X   X
		0x4444,  //  X   X   X   X
		0x8888,  // X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x4444,  //  X   X   X   X
		0x8888,  // X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x4444,  //  X   X   X   X
		0x8888,  // X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x4444,  //  X   X   X   X
		0x8888,  // X   X   X   X

		0x0000,  //
		0x2222,  //   X   X   X   X
		0x5555,  //  X X X X X X X X
		0x2222,  //   X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x5555,  //  X X X X X X X X
		0x2222,  //   X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x5555,  //  X X X X X X X X
		0x2222,  //   X   X   X   X
		0x0000,  //
		0x2222,  //   X   X   X   X
		0x5555,  //  X X X X X X X X
		0x2222,  //   X   X   X   X

		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //
		0x0000,  //

		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF,  // XXXXXXXXXXXXXXXX
		0xFFFF   // XXXXXXXXXXXXXXXX
	};

	private class PatternChoices extends JPanel
		implements MouseListener
	{
		private static final int NUMROWS = 2;
        private static final int ROWSIZE = 17;
		int numPatterns = preDefinedPatterns.length / 16;
        int yEntry = -1, xEntry = -1;

		PatternChoices()
		{
			addMouseListener(this);
			setMaximumSize(new Dimension(numPatterns*ROWSIZE/NUMROWS+1, ROWSIZE*NUMROWS+1));
			setMinimumSize(new Dimension(numPatterns*ROWSIZE/NUMROWS+1, ROWSIZE*NUMROWS+1));
			setPreferredSize(new Dimension(numPatterns*ROWSIZE/NUMROWS+1, ROWSIZE*NUMROWS+1));
		}

		/**
		 * Method to repaint this PatternChoices.
		 */
		public void paint(Graphics g)
		{
			ImageIcon icon = Resources.getResource(getClass(), "IconLayerPatterns.gif");
			g.drawImage(icon.getImage(), 0, 0, null);
            if (yEntry != -1 && xEntry != -1)
            {
                g.setColor(Color.BLACK);
                // Simulating thick lines
                g.drawRect(xEntry*ROWSIZE-1, yEntry*ROWSIZE-1, ROWSIZE+1, ROWSIZE+1);
                g.drawRect(xEntry*ROWSIZE, yEntry*ROWSIZE, ROWSIZE, ROWSIZE);
                g.drawRect(xEntry*ROWSIZE+1, yEntry*ROWSIZE+1, ROWSIZE-1, ROWSIZE-1);
            }
		}

		// the MouseListener events
		public void mousePressed(MouseEvent evt)
		{
			if (currentLI == null || currentLI.justColor) return;
			xEntry = evt.getX() / ROWSIZE;
			if (xEntry >= numPatterns/NUMROWS) xEntry = numPatterns/NUMROWS-1;
			yEntry = evt.getY() / ROWSIZE;
			if (yEntry >= NUMROWS) yEntry = NUMROWS-1;
			int iconIndex = xEntry + 11*yEntry;
			for(int i=0; i<16; i++)
			{
				currentLI.pattern[i] = preDefinedPatterns[iconIndex*16+i];
			}

			// fake a check in the stipple use
			useStipplePatternDisplay.setSelected(true);
			outlinePattern.setEnabled(true);
			currentLI.useStippleDisplay = true;

            paint(this.getGraphics());
			patternView.repaint();
			colorPreviewPanel.repaint();
		}
		public void mouseReleased(MouseEvent evt) {}
		public void mouseClicked(MouseEvent evt) {}
		public void mouseEntered(MouseEvent evt) {}
		public void mouseExited(MouseEvent evt) {}
	}

    private static final int SAMPLEWID = 60;
    private static final int SAMPLEHEI = 11;

    public ImageIcon getSample(Outline o) {
        // construct a sample of this outline texture
        int pattern = o.getPattern();
        int len = o.getLen();
        int thickness = o.getThickness();

        BufferedImage bi = new BufferedImage(SAMPLEWID+SAMPLEHEI, SAMPLEHEI, BufferedImage.TYPE_INT_RGB);
        int startX = SAMPLEHEI / 2;
        int startY = (SAMPLEHEI-thickness) / 2;
        for(int y=0; y<SAMPLEHEI; y++)
            for(int x=0; x<SAMPLEWID+SAMPLEHEI; x++)
                bi.setRGB(x, y, 0xFFFFFF);
        for(int x=0; x<SAMPLEWID+SAMPLEHEI; x++) {
            bi.setRGB(x, 0, 0);
            bi.setRGB(x, SAMPLEHEI-1, 0);
        }
        for(int y=0; y<SAMPLEHEI; y++) {
            bi.setRGB(0, y, 0);
            bi.setRGB(SAMPLEWID+SAMPLEHEI-1, y, 0);
        }
        for(int y=0; y<thickness; y++) {
            int patPos = 0;
            for(int x=0; x<SAMPLEWID; x++) {
                if ((pattern & (1<<patPos)) != 0) bi.setRGB(x+startX, y+startY, 0); else
                    bi.setRGB(x+startX, y+startY, 0xFFFFFF);
                patPos++;
                if (patPos >= len) patPos = 0;
            }
        }
        return new ImageIcon(bi);
    }

   /** This method is called from within the constructor to
    * initialize the form.
    * WARNING: Do NOT modify this code. The content of this method is
    * always regenerated by the Form Editor.
    */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents()
    {
        java.awt.GridBagConstraints gridBagConstraints;

        pattern = new javax.swing.JPanel();
        jLabel50 = new javax.swing.JLabel();
        useStipplePatternDisplay = new javax.swing.JCheckBox();
        outlinePattern = new javax.swing.JComboBox();
        jLabel3 = new javax.swing.JLabel();
        opacityLabel = new javax.swing.JLabel();
        opacity = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        useStipplePatternPrinter = new javax.swing.JCheckBox();
        color = new javax.swing.JPanel();
        jLabel40 = new javax.swing.JLabel();
        transparentLayer = new javax.swing.JComboBox();

        setLayout(new java.awt.GridBagLayout());

        pattern.setLayout(new java.awt.GridBagLayout());

        pattern.setBorder(javax.swing.BorderFactory.createTitledBorder("Pattern"));
        jLabel50.setText("Click on a pattern below to use it above");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
        pattern.add(jLabel50, gridBagConstraints);

        useStipplePatternDisplay.setText("Use Fill Pattern on Screen");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
        pattern.add(useStipplePatternDisplay, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
        pattern.add(outlinePattern, gridBagConstraints);

        jLabel3.setText("Outline pattern:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        pattern.add(jLabel3, gridBagConstraints);

        opacityLabel.setText("Opacity:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.gridheight = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        pattern.add(opacityLabel, gridBagConstraints);

        opacity.setColumns(6);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.gridheight = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        pattern.add(opacity, gridBagConstraints);

        jLabel1.setText("0: Transparent");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        pattern.add(jLabel1, gridBagConstraints);

        jLabel2.setText("1: Opaque");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 6;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        pattern.add(jLabel2, gridBagConstraints);

        useStipplePatternPrinter.setText("Use Fill Pattern on Printer");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 7;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        pattern.add(useStipplePatternPrinter, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 1.0;
        add(pattern, gridBagConstraints);

        color.setLayout(new java.awt.GridBagLayout());

        color.setBorder(javax.swing.BorderFactory.createTitledBorder("Color"));
        jLabel40.setText("Transparency:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        color.add(jLabel40, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        color.add(transparentLayer, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridheight = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        add(color, gridBagConstraints);

    }// </editor-fold>//GEN-END:initComponents


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel color;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel40;
    private javax.swing.JLabel jLabel50;
    private javax.swing.JTextField opacity;
    private javax.swing.JLabel opacityLabel;
    private javax.swing.JComboBox outlinePattern;
    private javax.swing.JPanel pattern;
    private javax.swing.JComboBox transparentLayer;
    private javax.swing.JCheckBox useStipplePatternDisplay;
    private javax.swing.JCheckBox useStipplePatternPrinter;
    // End of variables declaration//GEN-END:variables

}
