//     $Id: DrawableModule.java,v 3.1 1998/12/01 14:18:57 te3d Exp $    
 ////////////////////////////////////////////////////////////////////////////
 //
 // File: DrawableModule.java
 //
 // Purpose: A DrawableModule is a graphical representation of
 //          a Module.  It can be zoomed or unzoomed.  Properties:
 // 
 //            o Elliptical shape
 //            o Name     - label
 //            o Stable   - green border is stable, red is unstable  // change
 //            o Editable - white background is yes, gray is no      // change
 //            o Nodes    - if zoomed, these form an internal circle
 //
 //          This is a base class, and can used by several modules.
 //          For that reason, we don't specify the Invoking modules.
 //
 // Authors:
 //   txe  Travis Emmitt  emmitt@virginia.edu
 //
 // Modifications:
 //   28-OCT-1998  txe  (v1) Initial creation (was screen.Module)
 //   29-OCT-1998  txe  Removed GetSelection
 //   03-NOV-1998  txe  Added comments, removed unused methods.
 //   06-NOV-1998  rgb  Added version control header info
 //
 //   13-NOV-1998  txe  (v2) Extends Module, added Exceptions
 //   16-NOV-1998  txe  Improved exceptions, comments
 //   19-NOV-1998  txe  Shrank nodes
 //
 //   30-NOV-1998  txe  (v3) Improved format, speed, contrast
 //
 ////////////////////////////////////////////////////////////////////////////
 import java.applet.*;
 import java.awt.*;
 public class DrawableModule extends Module {
   private DrawableNode drawable_nodes[];   // drawable nodes (for when zoomed)
   private int x;               // X coordinate of northwest corner
   private int y;                           // Y coordinate of northwest corner
   private int w;                           // width
   private int h;                           // height
   private boolean editable;                // true iff module is editable
   private boolean zoomed;                  // true iff module is zoomed
   private static final double
     NODE_SIZE         = 0.18;              // size of node relative to module
   private static final int
     MAX_LABEL_LEN     = 10,       // maximum printable length of label
     MIN_EDGE            = 1,        // minimum edge width
     NORMAL_MODE          = 0,      // normal viewing mode
     SHOW_ALL_NODES    = 1,        // mode option: show all nodes
     DEPENDEE_MODE     = 2,      // mode option: we're a dependee
     DEPENDER_MODE     = 3;      // mode option: we're a depender
   private static final Color
     LABEL_COLOR       = new Color (0.0f, 0.0f, 0.0f), // black
     STABLE_FCOLOR     = new Color (0.0f, 0.3f, 0.0f), // dk green
     STABLE_BCOLOR     = new Color (0.8f, 1.0f, 0.8f), // lt green
     UNSTABLE_FCOLOR   = new Color (0.3f, 0.0f, 0.0f), // dk red
     UNSTABLE_BCOLOR   = new Color (1.0f, 0.8f, 0.8f), // lt red
     EDITABLE_BCOLOR   = new Color (0.9f, 0.9f, 1.0f), // white
     UNEDITABLE_BCOLOR = null,                         // background
     DEPENDEE_FCOLOR   = new Color (1.0f, 1.0f, 1.0f), // white
     DEPENDEE_BCOLOR   = new Color (0.8f, 0.0f, 0.0f), // red
     DEPENDER_FCOLOR   = new Color (0.0f, 0.2f, 0.0f), // dk green
     DEPENDER_BCOLOR   = new Color (1.0f, 1.0f, 0.3f); // yellow
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  DrawableModule  [constructor]
   //
   // Purpose: This creates a new DrawableModule representing the
   //          specified Module, with the specified bounds,
   //          editability, and zoom level.
   //   
   ///////////////////////////////////////////////////////////////////
   public DrawableModule (Module new_module, int new_bounds[],
              boolean new_editable, boolean new_zoomed)
   throws BoundsException, IndexException, NewException, NullException {
     if (new_module == null) {
       throw new NullException ("new_module");
     }
     if (new_bounds == null) {
       throw new NullException ("new_bounds");
     }
     Copy (new_module);
     int i = 0;
     try {
       x = new_bounds[i++];
       y = new_bounds[i++];
       w = new_bounds[i++];
       h = new_bounds[i++];
     }
     catch (Exception e) {
       throw new IndexException ("new_bounds", i);
     }
     editable = new_editable;
     zoomed   = new_zoomed;
     // Create and calculate positions of Nodes //
     try {
       drawable_nodes = new DrawableNode [num_nodes];
     }
     catch (Exception e) {
       throw new NewException ("drawable_nodes", num_nodes);
     }
     int node_w    = (int) (w * NODE_SIZE);
     int node_h    = (int) (node_w * 0.75);
     int base_x    = GetCenterX() - (node_w / 2);
     int base_y    = GetCenterY() - (node_h / 2);
     double radius = (double) w * 0.32;
     double angle  = Math.PI / 2;
     double delta  = 2 * Math.PI / (double) Math.max (num_nodes, 1);
     if (zoomed) {
       base_y += (h / 15);
     }
     else {
       radius = radius * 0.8;
     }
     for (i = 0; i < num_nodes; i++) {
       int node_x        = base_x + (int) (radius * Math.cos (angle));
       int node_y        = base_y - (int) (radius * Math.sin (angle));
       int node_bounds[] = { node_x, node_y, node_w, node_h };
       drawable_nodes[i] = new DrawableNode (nodes[i], node_bounds);
       angle -= delta;
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  Draw
   //
   // Purpose: This displays the Module (including its DrawableNodes)
   //          on the screen.
   //   
   ///////////////////////////////////////////////////////////////////
   public void Draw (Graphics graphics, int mode)
   throws BoundsException, GraphicsException, IndexException, NullException {
     if (graphics == null) {
       throw new NullException ("graphics");
     }
     int font_size = (int) (2 * w / MAX_LABEL_LEN);
     int label_len = Math.min (name.length(), MAX_LABEL_LEN);
     int    label_y   = GetCenterY() + (int) (font_size / 2);
     if (zoomed) {
       font_size   = font_size / 2;
       label_y     = GetCenterY() - (int) (h * .365);
     }
     int label_x    = GetCenterX() - ((font_size * label_len) / 4);
     String label   = name.substring (0, label_len);
     boolean stable = IsStable ();
     Color edge_color  = null;
     Color fill_color  = null;
     Color label_color = null;
     if (mode == SHOW_ALL_NODES) {
       edge_color  = (stable ? STABLE_FCOLOR : UNSTABLE_FCOLOR);
       fill_color  = (stable ? STABLE_BCOLOR : UNSTABLE_BCOLOR);
       label_color = null;
     }
     else if (mode == DEPENDEE_MODE) {
       edge_color  = DEPENDEE_FCOLOR;
       fill_color  = DEPENDEE_BCOLOR;
       label_color = edge_color;
     }
     else if (mode == DEPENDER_MODE) {
       edge_color  = DEPENDER_FCOLOR;
       fill_color  = DEPENDER_BCOLOR;
       label_color = edge_color;
     }
     else {
       edge_color  = (stable   ? STABLE_FCOLOR   : UNSTABLE_FCOLOR);
       fill_color  = (editable ? EDITABLE_BCOLOR : UNEDITABLE_BCOLOR);
       label_color = LABEL_COLOR;
     }
     try {
       if (fill_color != null) {
     graphics.setColor (fill_color);
     graphics.fillOval (x, y, w, h);
       }
       graphics.setColor (edge_color);
       graphics.drawOval (x, y, w, h);
       if (label_color != null) {
     graphics.setColor   (label_color);
     graphics.setFont    (new Font ("Helvetica", Font.PLAIN, font_size));
     graphics.drawString (label, label_x, label_y);
       }
     }
     catch (Exception e) {
       throw new GraphicsException (e);
     }
     if (zoomed || mode == SHOW_ALL_NODES) {
       for (int i = 0; i < num_nodes; i++) {
     GetDrawableNode(i).Draw (graphics, mode);
       }
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  GetCenterX
   //
   // Purpose: Returns X coordinate of center of DrawableModule.
   //   
   ///////////////////////////////////////////////////////////////////
   public int GetCenterX () {
     return x + (w / 2);
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  GetCenterX
   //
   // Purpose: Returns X coordinate of center of specified 
   //          DrawableNode.
   //   
   ///////////////////////////////////////////////////////////////////
   public int GetCenterX (int node_id)
   throws BoundsException, IndexException, NullException {
     if (node_id < 0) {
       return GetCenterX();
     }
     return GetDrawableNode(node_id).GetCenterX ();
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  GetCenterY
   //
   // Purpose: Returns Y coordinate of center of Module.
   //   
   ///////////////////////////////////////////////////////////////////
   public int GetCenterY () {
     return y + (h / 2);
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  GetCenterY
   //
   // Purpose: Returns Y coordinate of center of specified Node.
   //   
   ///////////////////////////////////////////////////////////////////
   public int GetCenterY (int node_id)
   throws BoundsException, IndexException, NullException {
     if (node_id < 0) {
       return GetCenterY();
     }
     return GetDrawableNode(node_id).GetCenterY ();
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  GetDrawableNode
   //
   // Purpose: Returns reference to specified DrawableNode.
   //   
   ///////////////////////////////////////////////////////////////////
   public DrawableNode GetDrawableNode (int node_id)
   throws BoundsException, IndexException, NullException {
     if (node_id < 0 || node_id >= num_nodes) {
       throw new BoundsException ("node_id", node_id, 0, num_nodes - 1);
     }
     DrawableNode drawable_node = null;
     try {
       drawable_node = drawable_nodes[node_id];
     }
     catch (Exception e) {
       throw new IndexException ("drawable_nodes", node_id);
     }
     if (drawable_node == null) {
       throw new NullException ("drawable_nodes", node_id);
     }
     return drawable_node;
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  Inside
   //
   // Purpose: This returns true if the specified coordinate
   //          is within the DrawableModule, false otherwise.
   //   
   ///////////////////////////////////////////////////////////////////
   public boolean Inside (int new_x, int new_y) {
     double dx = new_x - GetCenterX ();
     double dy = new_y - GetCenterY ();
     dy = (double) (dy * w) / (double) Math.max (h, 1);
     return (Math.sqrt ((dx * dx) + (dy * dy)) < (double) w / 2);
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Method:  InsideNode
   //
   // Purpose: This returns true if the specified coordinate is within
   //          the bounds of the specified DrawableNode, else false.
   //   
   ///////////////////////////////////////////////////////////////////
   public boolean InsideNode (int node_id, int x, int y)
   throws BoundsException, IndexException, NullException {
     if (!zoomed) {
       return false;
     }
     return GetDrawableNode(node_id).Inside (x, y);
   }
 }
 ////////////////////////////////////////////////////////////////////////////