
/*

   The class MosaicFrame represents a window subdivided into a grid of
   rectangles, where the color of each rectangle can be set individually.
   The user can close the window by clicking on it's close box.  The
   window can also be closed by calling the method close().  Once a
   window is closed, commands for setting rectangle colors will be ignored.

   David Eck, eck@hws.edu, September 1996

*/

import java.awt.*;

public class MosaicFrame extends Frame {

   public MosaicFrame() {
          // default constructor just provides values to main constructor
      this("Mosaic",20,20,200,200);
   }
   
   public MosaicFrame(String title, int ROWS, int COLUMNS) {
          // constructor call main constructor, providing default
          // values for WIDTH and HEIGHT that will give colored
          // squares that are 10-by-10 pixels big
      this(title,ROWS,COLUMNS,ROWS*10,COLUMNS*10);
   }
   
   public MosaicFrame(String title, int ROWS, int COLUMNS, int HEIGHT, int WIDTH) {
          // The main constructor for the class opens a window with the given
          // title.  The window displays a grid of colored rectangles.  The number
          // of rows and columns in the grid are specified by the parameters
          // ROWS and COLUMNS.  WIDTH and HEIGHT give the desired size of the
          // window.  WIDTH should be a multiple of COLUMNS, and HEIGHT should
          // be a multiple of ROWS (if not, they will be adjusted).
      super(title);
      if (WIDTH > 400)   // sanity check for values of WIDTH and HEIGHT
         WIDTH = 400;
      else if (WIDTH < COLUMNS)
         WIDTH = COLUMNS;
      if (HEIGHT > 400)
         HEIGHT = 400;
      else if (HEIGHT < ROWS)
         HEIGHT = ROWS;
      rows = ROWS;
      columns = COLUMNS;
      rectWidth = WIDTH / columns;
      width = rectWidth * columns;  // actual width is a multiple of rectWidth
      rectHeight = HEIGHT / rows;
      height = rectHeight * rows;   // actual height is a multiple of rectHeight
      resize(width,height);
      setResizable(false);  // window cannot be resized
      setBackground(Color.black);
      color = new Color[rows][columns];
      for (int i=0; i<rows; i++)
         for (int j=0; j<columns; j++)
            color[i][j] = null;
      show();
      g = getGraphics();
   }
   
   public boolean hasClosed() {
         // test whether window has been closed, either by user or by the close() method
      return closed;
   }
   
   public void close() {
         // close the window
      if (!closed) {
         dispose();
         closed = true;
      }
   }
   
   public static void delay(int milliseconds) {
         // wait, doing nothing, for the given number of milliseconds
      if (milliseconds > 0) {
         try { Thread.sleep(milliseconds); }
         catch (InterruptedException e) { }
      }
      else
         Thread.yield();
   }
      
   public void clear() {
         // set all the squares equal to the background color (which is black, unless
         // it has been changed by a call to setBackground()
      if (closed)
         return;
      for (int i=0; i<rows; i++)
         for (int j=0; j<columns; j++)
            color[i][j] = null;
      Color c = getBackground();
      g.setColor(c);
      g.fillRect(0,0,width,height);
   }
   
   public void setColor(int row, int column, Color c) {
          // set the color of the rectangle in given row and column
      if (closed || row < 0 || row >= rows || column < 0 || column >= columns)
         return;
      color[row][column] = c;
      g.setColor(c);
      g.fillRect(column*rectWidth,row*rectHeight, rectWidth, rectHeight);
   }
   
   public Color getColor(int row, int column) {
          // read the color of the rectangle in the given row and column
      if (closed)
         return null;
      if (color[row][column] != null)
         return color[row][column];
      else
         return getBackground();
   }
   
   public double getRed(int row, int column) {
          // Get the red component of the color in given row and column.
          // The value returned is between 0.0 and 1.0, with 0.0 indicating
          // that the color contains no red at all, and 1.0 indicating that
          // it contains the maximum possible amount of red.
      if (closed)
         return 0;
      Color c = getColor(row,column);
      return c.getRed() / 255.0;
   }
   
   public double getGreen(int row, int column) {
          // Get the green component of the color in given row and column.
          // The value returned is between 0.0 and 1.0, with 0.0 indicating
          // that the color contains no green at all, and 1.0 indicating that
          // it contains the maximum possible amount of green.
      if (closed)
         return 0;
      Color c = getColor(row,column);
      return c.getGreen() / 255.0;
   }
   
   public double getBlue(int row, int column) {
          // Get the blue component of the color in given row and column.
          // The value returned is between 0.0 and 1.0, with 0.0 indicating
          // that the color contains no blue at all, and 1.0 indicating that
          // it contains the maximum possible amount of blue.
      if (closed)
         return 0;
      Color c = getColor(row,column);
      return c.getBlue() / 255.0;
   }
   
   public void setColor(int row, int column, double red, double green, double blue) {
          // Set the color of the rectangle in the given row and column.
          // The parameters "red", "blue", and "green" should be between
          // 0.0 and 1.0 (if not, they will be adjusted to lie in that range).
          // They specify the level of red, blue, and green in the color.
      if (closed)
         return;
      if (red < 0.0)
         red = 0.0;
      else if (red > 1.0)
         red = 1.0;
      if (green < 0.0)
         green = 0.0;
      else if (green > 1.0)
         green = 1.0;
      if (blue < 0.0)
         blue = 0.0;
      else if (blue > 1.0)
         blue = 1.0;
      Color c = new Color((float)red, (float)green, (float)blue);
      setColor(row,column,c);
   }
   
   public void setAll(double red, double green, double blue) {
          // Set the color of all the squares in the window to the same value.
          // The parameters "red", "blue", and "green" should be between
          // 0.0 and 1.0 (if not, they will be adjusted to lie in that range).
          // They specify the level of red, blue, and green in the color.
      if (closed)
         return;
      if (red < 0.0)
         red = 0.0;
      else if (red > 1.0)
         red = 1.0;
      if (green < 0.0)
         green = 0.0;
      else if (green > 1.0)
         green = 1.0;
      if (blue < 0.0)
         blue = 0.0;
      else if (blue > 1.0)
         blue = 1.0;
      Color c = new Color((float)red, (float)green, (float)blue);
      for (int row = 0; row < rows; row++)
         for (int col = 0; col < columns; col++)
            color[row][col] = c;
      g.setColor(c);
      g.fillRect(0,0,width,height);
   }

   public void paint(Graphics g) {
          // This is called by the system when the window needs to be
          // repainted.  It is not meant to be called by a program.
      if (color == null)
         g.clearRect(0,0,size().width,size().height);
      else {
         for (int col = 0; col < columns; col++)
            for (int row = 0; row < rows; row++)
               if (color[row][col] != null) {
                  g.setColor(color[row][col]);
                  g.fillRect(col*rectWidth,row*rectHeight, rectWidth, rectHeight);
               }
      }
   }
   
   public boolean handleEvent(Event evt) {
          // This is called by the system when some event occurs.
          // It is not meant to be called by a program.
      if (evt.id == Event.WINDOW_DESTROY) {
         closed = true;  // user has clicked window's close box
         dispose();
         return true;
      }
      else
         return super.handleEvent(evt);
   }
      
   private int rows;                 // number of rows of squares
   private int columns;              // number of columns
   private  Color[][] color;         // color of each rectangle
   private int height = 0;           // height of window
   private int width = 0;            // width of window
   private int rectHeight;         // height of each individual rectangle
   private int rectWidth;          // width of each individual rectangle
   private Graphics g;               // graphics context for the window
   private boolean closed = false;   // set to true when window is closed

}  // end of class MosaicFrame
