/*

@(#) $Id: BaseRGBMap.java,v 1.22 2002-09-19 21:08:42 curtis Exp $

VisAD Utility Library: Widgets for use in building applications with
the VisAD interactive analysis and visualization library
Copyright (C) 2017 Nick Rasmussen
VisAD is Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink and Dave Glowacki.

This program 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 1, or (at your option)
any later version.

This program 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 in file NOTICE for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package visad.util;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Toolkit;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.rmi.RemoteException;

import visad.BaseColorControl;
import visad.ControlEvent;
import visad.ControlListener;
import visad.VisADException;

/**
 * An extensible RGB colormap with no interpolation between the
 * internally stored values.  Click and drag with the left mouse
 * button to draw the color curves. Click with the right or middle
 * mouse button to alternate between the color curves.
 *
 * @author Nick Rasmussen nick@cae.wisc.edu
 * @version $Revision: 1.22 $, $Date: 2002-09-19 21:08:42 $
 * @since Visad Utility Library, 0.5
 */

public class BaseRGBMap
  extends ColorMap
  implements ControlListener, MouseListener, MouseMotionListener
{
  /** change this to true to use color cursors */
  public static boolean USE_COLOR_CURSORS = false;

  /** default resolution */
  public static final int DEFAULT_RESOLUTION =
    BaseColorControl.DEFAULT_NUMBER_OF_COLORS;

  /** The color control */
  private BaseColorControl ctl;

  /** The left modified value */
  private int valLeft = 0;
  /** The right modified value */
  private int valRight = 0;

  /** A lock to synchronize against when modifying the modified area */
  private Object mutex = new Object();

  /** The index of the color red */
  private static final int RED = BaseColorControl.RED;
  /** The index of the color green */
  private static final int GREEN = BaseColorControl.GREEN;
  /** The index of the color blue */
  private static final int BLUE = BaseColorControl.BLUE;
  /** The index of the alpha channel */
  private static final int ALPHA = BaseColorControl.ALPHA;
  /** The current color for the mouse to draw on */
  private int state = RED;

  /** The resolution of the map */
  private int resolution = 256;
  /** 'true' if this map has an alpha component */
  private boolean hasAlpha;

  private static Cursor[] cursor = null;
  /** a slightly brighter blue */
  private static final Color bluish = new Color(80, 80, 255);

  /** the preferred size of this widget */
  private Dimension preferredSize = null;

  /**
   * Construct a BaseRGBMap with the default resolution
   *
   * @param hasAlpha set to true is this map has
   *                 an alpha component
   */
  public BaseRGBMap(boolean hasAlpha)
    throws RemoteException, VisADException
  {
    this(defaultTable(DEFAULT_RESOLUTION, hasAlpha));
  }

  /**
   * Construct a colormap with the specified resolution
   *
   * @param resolution the length of the array
   * @param hasAlpha set to true is this map has
   *                 an alpha component
   */
  public BaseRGBMap(int resolution, boolean hasAlpha)
    throws RemoteException, VisADException
  {
    this(defaultTable(resolution, hasAlpha));
  }

  /**
   * Construct a colormap initialized with the supplied tuples
   *
   * @param vals the tuples used to initialize the colormap
   * @param hasAlpha true if the colormap should
   *                 have an ALPHA component.
   *
   * @deprecated hasAlpha isn't really necessary.
   */
  public BaseRGBMap(float[][] vals, boolean hasAlpha)
    throws RemoteException, VisADException
  {
    this(vals != null ? vals : defaultTable(DEFAULT_RESOLUTION, hasAlpha));
  }

  /**
   * Construct a colormap initialized with the supplied tuples
   *
   * @param vals the tuples used to initialize the colormap
   *             See setValues() for constraints on the vals array.
   */
  public BaseRGBMap(float[][] vals)
    throws RemoteException, VisADException
  {
    if (vals == null) {
      vals = defaultTable(DEFAULT_RESOLUTION, true);
    }

    setValues(vals);

    if (USE_COLOR_CURSORS) buildCursors();

    addMouseListener(this);
    addMouseMotionListener(this);
    ctl.addControlListener(this);
  }

  /**
   * Construct a colormap from the specified control.
   *
   * @param ctl control to use as data source
   */
  public BaseRGBMap(BaseColorControl ctl)
  {
    this.ctl = ctl;

    hasAlpha = (ctl.getNumberOfComponents() == 4);
    resolution = ctl.getNumberOfColors();

    if (USE_COLOR_CURSORS) buildCursors();

    addMouseListener(this);
    addMouseMotionListener(this);
    ctl.addControlListener(this);
  }

  /**
   * Build a table with the given number of components and colors
   *
   * @param resolution Number of colors.
   * @param hasAlpha true if this colormap has an alpha component.
   *
   * @return The new table.
   */
  static float[][] defaultTable(int resolution, boolean hasAlpha)
  {
    final int components = hasAlpha ? 4 : 3;
    float[][] tbl = new float[components][resolution];
    return BaseColorControl.initTableVis5D(tbl);
  }

  /**
   * Build one of the red, green, blue and alpha cursors
   *
   * @param rgba cursor to build (RED, GREEN, BLUE or ALPHA)
   *
   * @return the new Cursor
   */
  static Cursor buildRGBACursor(int rgba)
  {
    if (rgba < 0 || rgba > 3) rgba = 0;

    final int lines = 15;
    final int elements = 15;

    int[] pixel = new int[lines*elements];

    for (int i = 0; i < pixel.length; i++) {
      pixel[i] = 0;
    }

    final int color;
    switch (rgba) {
    case RED: color = Color.red.getRGB(); break;
    case GREEN: color = Color.green.getRGB(); break;
    case BLUE: color = bluish.getRGB(); break;
    default:
    case ALPHA: color = Color.gray.getRGB(); break;
    }

    final int midLine = (lines / 2) * elements;
    for (int i = midLine + elements - 1; i >= midLine; i--) {
      pixel[i] = color;
    }

    final int midElement = (elements / 2);
    for (int i = 0; i < lines; i++) {
      pixel[i*elements + midElement] = color;
    }
    java.awt.image.ImageProducer ip;
    ip = new java.awt.image.MemoryImageSource(elements, lines, pixel,
                                              0, elements);

    java.awt.Image img = Toolkit.getDefaultToolkit().createImage(ip);

    Point pt = new Point(img.getWidth(null) / 2, img.getHeight(null) / 2);
    String name;
    switch (rgba) {
    case RED: name = "crossRed"; break;
    case GREEN: name = "crossGreen"; break;
    case BLUE: name = "crossBlue"; break;
    default:
    case ALPHA: name = "crossAlpha"; break;
    }

    return Toolkit.getDefaultToolkit().createCustomCursor(img, pt, name);
  }

  /**
   * Used internally to initialize the red, green, blue and alpha cursors
   */
  private void buildCursors()
  {
    if (cursor != null) return;

    // only try to change the cursor if we're running under JDK 1.3 or greater
    String jVersion = System.getProperty("java.version");
    if (jVersion == null) return;
    if (jVersion.length() < 3) return;
    if (jVersion.charAt(0) < '1') return;
    if (jVersion.charAt(1) != '.') return;
    if (jVersion.charAt(0) == '1' && jVersion.charAt(2) < '3') return;

    cursor = new Cursor[4];
    cursor[RED] = buildRGBACursor(RED);
    cursor[GREEN] = buildRGBACursor(GREEN);
    cursor[BLUE] = buildRGBACursor(BLUE);
    cursor[ALPHA] = buildRGBACursor(ALPHA);

    setCursor(cursor[state]);
  }

  /**
   * Sets the values of the internal array after the map
   * has been created.
   *
   * The table should be float[resolution][dimension]
   * where dimension is either 3 (for an RGB table)
   * or 4 (if the table also has an alpha component) and
   * resolution is the number of colors in the table,
   * which must be greater than 4.
   *
   * @param newVal the color tuples used to initialize the map.
   */
  public void setValues(float[][] newVal)
    throws RemoteException, VisADException
  {
    if (newVal == null) {
      throw new VisADException("Can't set table to null");
    }

    if (newVal.length >= 3 && newVal.length <= 4 && newVal[0].length > 4) {
      hasAlpha = newVal.length > 3;
      resolution = newVal[0].length;
    } else if (newVal[0].length >= 3 && newVal[0].length <= 4 &&
               newVal.length > 4)
    {
      // table is inverted

      hasAlpha = newVal[0].length > 3;
      resolution = newVal.length;

      float[][] tmpVal = new float[hasAlpha ? 4 : 3][resolution];
      for (int i = 0; i < resolution; i++) {
        tmpVal[RED][i] = newVal[i][RED];
        tmpVal[GREEN][i] = newVal[i][GREEN];
        tmpVal[BLUE][i] = newVal[i][BLUE];
        if (hasAlpha) {
          tmpVal[ALPHA][i] = newVal[i][ALPHA];
        }
      }
      newVal = tmpVal;
    } else {
      throw new VisADException("Cannot set table with dimensions [" +
                               newVal.length + "][" + newVal[0].length + "]");
    }

    if (ctl == null) {
      ctl = new BaseColorControl(null, hasAlpha ? 4 : 3);
    }

    ctl.setTable(newVal);

    sendUpdate(0, resolution-1);
  }

  /**
   * Get the resolution of the map
   *
   * @return the number of colors in the map.
   */
  public int getMapResolution() {
    return resolution;
  }

  /**
   * Get the dimension of the map
   *
   * @return either 3 or 4
   */
  public int getMapDimension() {
    return ctl.getNumberOfComponents();
  }

  /**
   * Get the color map (as an array of float tuples.
   *
   * @return a copy of the color map
   */
  public float[][] getColorMap() {
    return ctl.getTable();
  }

  /**
   * Returns the tuple at a floating point value val
   *
   * @param firstVal the location to start.
   * @param lastVal the location to finish.
   * @param num the number of tuples to return.
   *
   * @return The array of 3 or 4 element arrays.
   */
  public float[][] getTuples(float firstVal, float lastVal, int num) {
    if (num <= 0 || firstVal > lastVal ||
        (num == 1 && !Util.isApproximatelyEqual(firstVal, lastVal)))
    {
      return null;
    }

    float floatIdx;

    floatIdx = firstVal * (resolution - 1);
    int startIndex = (int )Math.floor(floatIdx);

    floatIdx = lastVal * (resolution - 1);
    int endIndex = (int )Math.floor(floatIdx);

    float partialEnd = floatIdx - endIndex;
    boolean isPartialEnd = (partialEnd != 0);

    float[][] tuples = new float[num][hasAlpha ? 4 : 3];

    int rStart = startIndex;
    if (rStart < 0) {
      rStart = 0;
    } else if (rStart >= resolution) {
      rStart = resolution - 1;
    }

    int rEnd = (isPartialEnd ? endIndex+1 : endIndex);
    if (rEnd >= resolution) {
      rEnd = resolution - 1;
    } else if (rEnd < 0) {
      rEnd = 0;
    }

    final int rLen = (rEnd - rStart) + 1;

    float[][] colors;
    try {
      colors = ctl.lookupRange(rStart, rEnd);
    } catch (Exception e) {
      System.err.println("Error in " + getClass().getName() + ": " +
                         e.getClass().getName() + ": " + e.getMessage());
      return null;
    }

    float stepVal = (lastVal - firstVal) / (float )num;

    for (int i = 0; i < num; i++) {
      float thisVal = firstVal + (float )i * stepVal;

      floatIdx = thisVal * (resolution - 1);
      int index = (int )Math.floor(floatIdx);

      float partial = floatIdx - index;
      boolean isPartial = (partial != 0);

      index -= rStart;

      if (index < 0 || index >= rLen ||
          (index == (rLen - 1) && isPartial))
      {
        tuples[i][RED] = tuples[i][GREEN] = tuples[i][BLUE] = 0;
        if (hasAlpha) {
          tuples[i][ALPHA] = 0;
        }
        continue;
      }

      if (isPartial) {
        tuples[i][RED] = colors[RED][index] * (1 - partial) +
          colors[RED][index+1] * partial;
        tuples[i][GREEN] = colors[GREEN][index] * (1 - partial) +
          colors[GREEN][index+1] * partial;
        tuples[i][BLUE] = colors[BLUE][index] * (1 - partial) +
          colors[BLUE][index+1] * partial;
        if (hasAlpha) {
          tuples[i][ALPHA] = colors[ALPHA][index] * (1 - partial) +
            colors[ALPHA][index+1] * partial;
        }
      } else {
        tuples[i][RED] = colors[RED][index];
        tuples[i][GREEN] = colors[GREEN][index];
        tuples[i][BLUE] = colors[BLUE][index];
        if (hasAlpha) {
          tuples[i][ALPHA] = colors[ALPHA][index];
        }
      }
    }

    return tuples;
  }

  /**
   * Returns the tuple at a floating point value val
   *
   * WARNING: This is a really slow way to
   * get a color, so don't use it inside a loop.
   *
   * @param value the location to return.
   *
   * @return The 3 or 4 element array.
   */
  public float[] getTuple(float value) {
    float[][] v = getTuples(value, value, 1);
    return (v == null ? null : v[0]);
  }

  /**
   * Implementation of the abstract function in ColorMap
   *
   * WARNING: This is a really slow way to
   * get a color, so don't use it inside a loop.
   */
  public float[][] getRGBTuples(float startVal, float endVal, int num) {
    float[][] t = getTuples(startVal, endVal, num);
    if (t[0].length > 3) {
      float[][] f = new float[t.length][3];
      for (int i = t.length - 1; i >= 0; i--) {
        f[i][RED] = t[i][RED];
        f[i][GREEN] = t[i][GREEN];
        f[i][BLUE] = t[i][BLUE];
      }
      t = f;
    }
    return t;
  }

  /**
   * Implementation of the abstract function in ColorMap
   *
   * WARNING: This is a really slow way to
   * get a color, so don't use it inside a loop.
   *
   * @param value a floating point number between 0 and 1
   * @return an RGB tuple of floating point numbers in the
   * range 0 to 1
   */
  public float[] getRGBTuple(float value) {
    float[] t = getTuple(value);
    if (t.length > 3) {
      float[] f = new float[3];
      f[RED] = t[RED];
      f[GREEN] = t[GREEN];
      f[BLUE] = t[BLUE];
      t = f;
    }
    return t;
  }

  /**
   * Redraw the between the left and
   * right colors
   *
   * @param left the left edge of the changed area (in the range 0.0-1.0)
   * @param right the right edge of the changed area
   */
  protected void sendUpdate(int left, int right) {

    notifyListeners(new ColorChangeEvent(this, left, right));

    if (left != 0) {
      left--;
    }
    if (right != resolution - 1) {
      right++;
    }

    synchronized (mutex) {
      if (left < valLeft)
        valLeft = left;
      if (right > valRight)
        valRight = right;
    }

    // redraw
    validate();
    repaint();
  }

  /**
   * Present to implement MouseListener, currently ignored
   *
   * @param evt ignored
   */
  public void mouseClicked(MouseEvent evt) {
  }

  /**
   * MouseListener, currently ignored
   *
   * @param evt ignored
   */
  public void mouseEntered(MouseEvent evt) {
  }

  /**
   * MouseListener method, currently ignored
   *
   * @param evt ignored
   */
  public void mouseExited(MouseEvent evt) {
  }

  /** The last mouse event's x value */
  private int oldX;
  /** The last mouse event's y value */
  private int oldY;

  /** A synchronization primitive for the mouse movements */
  private Object mouseMutex = new Object();

  /**
   * Updates the associated Control
   *
   * @param evt the mouse press event
   */
  public void mousePressed(MouseEvent evt) {
    if ((evt.getModifiers() & evt.BUTTON1_MASK) == 0 &&
        evt.getModifiers() != 0)
    {
      return;
    }

    int width = getBounds().width;
    int height = getBounds().height;
    int x = evt.getX();
    int y = evt.getY();

    if (x < 0)
      x = 0;
    else if (x >= width)
      x = width - 1;
    if (y < 0)
      y = 0;
    else if (y >= height)
      y = height - 1;

    float step = (float )(resolution - 1) / (float )width;
    int pos = (int )Math.floor((float )x * step + 0.5);

    float[][] colors;
    try {
      colors = ctl.lookupRange(pos, pos);
    } catch (Exception e) {
      System.err.println("Error in " + getClass().getName() + ": " +
                         e.getClass().getName() + ": " + e.getMessage());
      return;
    }

    colors[state][0] = 1 - (float )y / (float )height;

    try {
      ctl.setRange(pos, pos, colors);
    } catch (Exception e) {
      System.err.println("Error in " + getClass().getName() + ": " +
                         e.getClass().getName() + ": " + e.getMessage());
      return;
    }

    oldX = x;
    oldY = y;

    sendUpdate(pos, pos);
  }

  /**
   * Listens for releases of the right mouse button,
   * and changes the active color
   *
   * @param evt the release event
   */
  public void mouseReleased(MouseEvent evt) {
    if ((evt.getModifiers() & (evt.BUTTON2_MASK|evt.BUTTON3_MASK)) == 0) {
      return;
    }
    state = (state + 1) % (hasAlpha ? 4 : 3);
    if (cursor != null) {
      setCursor(cursor[state]);
    }
  }

  /**
   * Updates the associated Control
   *
   * @param evt the drag event
   */
  public void mouseDragged(MouseEvent evt) {
    if ((evt.getModifiers() & evt.BUTTON1_MASK) == 0 &&
        evt.getModifiers() != 0)
    {
      return;
    }

    drag(evt.getX(), evt.getY(), oldX, oldY);

    oldX = evt.getX();
    oldY = evt.getY();
  }

  /**
   * Internal mouse dragging function
   *
   * @param x the current x coordinate
   * @param y the current y coordinate
   * @param oldx the starting x coordinate
   * @param oldy the starting y coordinate
   */
  private void drag(int x, int y, int oldx, int oldy) {

    int width = getBounds().width;
    int height = getBounds().height;

    // make sure x, y, oldx and oldy are all inside the window
    if (x < 0)
      x = 0;
    else if (x >= width)
      x = width - 1;
    if (y < 0)
      y = 0;
    else if (y >= height)
      y = height - 1;
    if (oldx < 0)
      oldx = 0;
    else if (oldx >= width)
      oldx = width - 1;
    if (oldy < 0)
      oldy = 0;
    else if (oldy >= height)
      oldy = height - 1;

    float step = (float )(resolution - 1) / (float )width;

    int oldPos = (int )Math.floor((float )oldx * step + 0.5);
    int newPos = (int )Math.floor((float )x * step + 0.5);

    float oldVal = 1 - (float )oldy / (float )height;
    float newVal = 1 - (float )y / (float )height;

    final int start, finish;
    final int len = ctl.getNumberOfColors() - 1;
    if (newPos > oldPos) {
      start = oldx < width - 1 ? oldPos : len;
      finish = x < width - 1 ? newPos : len;
    } else {
      start = x < width - 1 ? newPos : len;
      finish = oldx < width - 1 ? oldPos : len;
    }

    float[][] colors;
    try {
      colors = ctl.lookupRange(start, finish);
    } catch (Exception e) {
      System.err.println("Error in " + getClass().getName() + ": " +
                         e.getClass().getName() + ": " + e.getMessage());
      return;
    }

    final float loVal, hiVal;

    if (newPos > oldPos) {
      loVal = newVal;
      hiVal = oldVal;
    } else {
      loVal = oldVal;
      hiVal = newVal;
    }

    final int total = finish - start + 1;
    for (int i = 0; i < total; i++) {
      float v = ((hiVal * (float )(total - i) + loVal * (float )i) /
                 (float )total);
      colors[state][i] = v;
    }

    try {
      ctl.setRange(start, finish, colors);
    } catch (Exception e) {
      System.err.println("Error in " + getClass().getName() + ": " +
                         e.getClass().getName() + ": " + e.getMessage());
      return;
    }

    sendUpdate(start, finish);
  }

  /**
   * MouseMovementListener method, currently ignored
   *
   * @param evt ignored
   */
  public void mouseMoved(MouseEvent evt) {
  }

  /**
   * Repaints the entire JPanel
   *
   * @param g The Graphics to update.
   */
  public void paint(Graphics g) {

    synchronized (mutex) {

      valLeft = 0;
      valRight = resolution - 1;
    }

    update(g);
  }

  /** The left bound for updating the JPanel */
  private float updateLeft = 0;

  /** The right bound for updating the JPanel */
  private float updateRight = 1;

  /**
   * Repaints the modified areas of the JPanel
   *
   * @param g The Graphics to update.
   */
  public void update(Graphics g) {

    final int maxRight = resolution - 1;

    int left = 0;
    int right = maxRight;

    synchronized (mutex) {
      if (valLeft > valRight) {
        return;
      }

      left = valLeft;
      right = valRight;

      valLeft = maxRight;
      valRight = 0;
    }

    final int numColors = ctl.getNumberOfColors() - 1;

    if (left < 0) {
      left = 0;
    } else if (left > numColors) {
      left = numColors;
    }
    if (right < 0) {
      right = 0;
    } else if (right > numColors) {
      right = numColors;
    }

    if (left > 0) {
      left--;
    }
    if (right < maxRight) {
      right++;
    }

    final int maxWidth = getBounds().width - 1;
    final int maxHeight = getBounds().height - 1;

    int leftPixel = (left * maxWidth) / maxRight;
    int rightPixel = (right * maxWidth) / maxRight;

    g.setColor(Color.black);
    g.fillRect(leftPixel,0,rightPixel - leftPixel + 1, maxHeight + 1);

    if (left > 0) {
      left--;
    }
    if (right < maxRight) {
      right++;
    }

    leftPixel = (left * maxWidth) / maxRight;
    rightPixel = (right * maxWidth) / maxRight;

    float[][] colors;
    try {
      colors = ctl.lookupRange(left, right < maxRight ? right + 1 : maxRight);
    } catch (Exception e) {
      System.err.println("Error in " + getClass().getName() + ": " +
                         e.getClass().getName() + ": " + e.getMessage());
      colors = null;
    }

    if (colors == null) {
      return;
    }

    int prevEnd = leftPixel;

    int prevRed = (int )Math.floor((1 - colors[RED][0]) * maxHeight);
    int prevGreen = (int )Math.floor((1 - colors[GREEN][0]) * maxHeight);
    int prevBlue = (int )Math.floor((1 - colors[BLUE][0]) * maxHeight);
    int prevAlpha;
    if (hasAlpha) {
      prevAlpha = (int )Math.floor((1 - colors[ALPHA][0]) * maxHeight);
    } else {
      prevAlpha = 0;
    }

    int alpha = 0;
    for (int i = 1; i < colors[0].length; i++) {
      int lineEnd = ((left + i) * maxWidth) / maxRight;

      int red = (int )Math.floor((1 - colors[RED][i]) * maxHeight);
      int green = (int )Math.floor((1 - colors[GREEN][i]) * maxHeight);
      int blue = (int )Math.floor((1 - colors[BLUE][i]) * maxHeight);
      if (hasAlpha) {
        alpha = (int )Math.floor((1 - colors[ALPHA][i]) * maxHeight);
      }

      g.setColor(Color.red);
      g.drawLine(prevEnd, prevRed, lineEnd, red);

      g.setColor(Color.green);
      g.drawLine(prevEnd, prevGreen, lineEnd, green);

      g.setColor(bluish);
      g.drawLine(prevEnd, prevBlue, lineEnd, blue);

      if (hasAlpha) {
        g.setColor(Color.gray);
        g.drawLine(prevEnd, prevAlpha, lineEnd, alpha);
      }

      prevEnd = lineEnd;

      prevRed = red;
      prevGreen = green;
      prevBlue = blue;
      if (hasAlpha) {
        prevAlpha = alpha;
      }
    }
  }

  /**
   * Return the preferred size of this map, taking into account
   * the resolution.
   *
   * @return preferred size.
   */
  public Dimension getPreferredSize()
  {
    if (preferredSize == null) {
      final int height = 170;
      preferredSize = new Dimension(resolution, height);
    }
    return preferredSize;
  }

  /**
   * Override the widget's calculation for the preferred size
   * of this map.
   *
   * @param pref preferred size.
   */
  public void setPreferredSize(Dimension pref)
  {
    preferredSize = pref;
  }

  /**
   * If the color data in the Control associated with this
   * widget's Control has changed, update the data in
   * the ColorMap.
   *
   * @param evt Data from the changed Control.
   */
  public void controlChanged(ControlEvent evt)
    throws RemoteException, VisADException
  {
    hasAlpha = (ctl.getNumberOfComponents() == 4);
    resolution = ctl.getNumberOfColors();

    sendUpdate(0, getMapResolution()-1);
  }
}

 

Ads help maintain this website.