Link to home
Start Free TrialLog in
Avatar of fivesigmaevent
fivesigmaevent

asked on

objects or other experts, extending JSlider component that overlaps another component?

Hi,

Some background. I'm learning Java and have been practicing writing apps for about a month now. Wrote GUI connect 4 and some client server stuff and feel that I am close to intermediate stage. Love the language. Now I'm trying to learn some of the more complicated stuff.

I just started reading about swing last night. Looks like the JSlider might be useful for what I'm trying to achieve. So, I'm trying to figure out how to extend a slider to have a vertical line attached to it so it slides left and right along with the slider. The slider's line would be overlapping a northeast quadrant of a cartesian plane. Yesterday I wrote the code below (will change it to swing later to be consistent) which can illustrate it. The red line would be attached to or somehow associated to a JSlider at the bottom of it.

Any suggestions on how to do this? Is this possible with JSlider, or is there a better approach? Can a component overlap another, ie, the line from the slider would be in the foreground and the northeast quadrant in the background? If the slider line sweeps across the quadrant's canvas, does the canvas require repainting?

Any pointers on how to accomplish this are very much appreciated.


// begin file
import java.awt.*;
import java.awt.event.*;

public class App extends Frame implements WindowListener
{
  Plane _p;
  private final int _WIDTH  = 500;
  private final int _HEIGHT = 520;
  Dimension _plane_size; // size of axis
  Point _offset;  // axis' offset from Canvas edges
  LineComponent _line; // component that shifts left or right, like a slidebar


  App()
  {
    super( "WHATEVER DUDE" );

    // call so that createImage() and setExtendedState() will work
//    addNotify();
    // maximize frame
//    setExtendedState( Frame.MAXIMIZED_BOTH );

     setSize( _WIDTH, _HEIGHT );


    // make a plane and add it to Frame
    _plane_size = new Dimension( 200, 200 );
    _offset     = new Point( 30, 30 );
    _line       = new LineComponent( new Point( 100, 30 ), new Point( 100, 250));

    _p = new Plane( _plane_size, _offset, _line );
    add( _p );

    // listen to window events
    addWindowListener( this );

    // show the window
    show();
  }

  void start()
  {
    _p.drawLineComponent();
    repaint();
  }


   public void windowClosing(WindowEvent e)
   {
      hide();
      dispose();
   }

   public void windowClosed(WindowEvent e){}

   public void windowOpened(WindowEvent e){}

   public void windowIconified(WindowEvent e){}

   public void windowDeiconified(WindowEvent e){}

   public void windowActivated(WindowEvent e){}

   public void windowDeactivated(WindowEvent e){}


  public static void main(String args[])
  {
    // instantiate the app
    App app;
    app = new App();
    app.start();
  }
}


class Plane extends Canvas
{
  private Dimension _dimension; // dimensions of plane
  private LineComponent _line;  // component that shifts left or right, like a slidebar
  private Point _offset; // offset from edges of border
  private Point [] _x_axis; // points for X axes; element 0 is left Point, elem 1 is right Point of axis
  private Point [] _y_axis; // points for Y axes; element 0 is top Point, elem 1 is bottom Point of axis
  private Image _img;       // in-mem image for double buffering draw technique
  private Graphics _gc;     // graphics context
  private boolean _first_time; // first time painting
  private final int TOP    = 0;
  private final int LEFT   = 0;
  private final int RIGHT  = 1;
  private final int BOTTOM = 1;



  Plane( Dimension d, Point offset, LineComponent line )
  {
    // save width, height and offset
    _dimension = d;
    _offset    = offset;
    _line      = line;

    _first_time = true;
    // setBackground( Color.GRAY );
    setSize( d.width + 60, d.height + 80 );

    // called so that createImage() will work
//    addNotify();

    // allocate space for references
    _y_axis = new Point[2];
    _x_axis = new Point[2];

    // save Points to X and Y axes
    // Y axis points
    _y_axis[0] = new Point( _offset.getX(), _offset.getY() );
    _y_axis[1] = new Point( _offset.getX(), _offset.getY() + _dimension.height );

    // X axis points
    _x_axis[0] = new Point( _offset.getX(), _offset.getY() + _dimension.height );
    _x_axis[1] = new Point( _offset.getX() + _dimension.width, _offset.getY() + _dimension.height );

  }


  public void paint( Graphics g )
  { // use double buffering to draw

    if( _first_time )
    { // first time painting, so create an image to draw to
      // tried to call addNotify() so I could create an Image and Graphics context
      // prior to this, but to no avail

      // double buffer to avoid repetitive drawing
      _img = createImage( 350, 350 );
      _gc  = _img.getGraphics();
      _first_time = false;

      // draw the axes
      drawAxes();

      // draws a Line Component
      drawLineComponent();
    }

    // _img is the in-mem buffer
    g.drawImage( _img, 0, 0, this );
  }


  // draws an X Y axis
  public void drawAxes()
  {
    int [] arrow_x;
    int [] arrow_y;

    // space for polygon
    arrow_x = new int [3];
    arrow_y = new int [3];

    // draw Y axis with arrow on top
    _gc.drawLine( _y_axis[0].getX(), _y_axis[0].getY(), _y_axis[1].getX(), _y_axis[1].getY() );
    arrow_x[0] = _y_axis[TOP].getX() - 3;
    arrow_x[1] = _y_axis[TOP].getX();
    arrow_x[2] = _y_axis[TOP].getX() + 3;
    arrow_y[0] = _y_axis[TOP].getY() + 5;
    arrow_y[1] = _y_axis[TOP].getY();
    arrow_y[2] = _y_axis[TOP].getY() + 5;
    _gc.drawPolygon( arrow_x, arrow_y, 3 );

    // draw X axis with arrow on right
    _gc.drawLine( _x_axis[0].getX(), _x_axis[0].getY(), _x_axis[1].getX(), _x_axis[1].getY() );
    arrow_x[0] = _x_axis[RIGHT].getX() - 5;
    arrow_x[1] = _x_axis[RIGHT].getX();
    arrow_x[2] = _x_axis[RIGHT].getX() - 5;
    arrow_y[0] = _x_axis[RIGHT].getY() + 3;
    arrow_y[1] = _x_axis[RIGHT].getY();
    arrow_y[2] = _x_axis[RIGHT].getY() - 3;
    _gc.drawPolygon( arrow_x, arrow_y, 3 );

    System.out.println( "drawing y and x axes" );
  }


  // draws a Line Component
  public void drawLineComponent()
  {
    Color old_color;
    old_color = _gc.getColor();

    _gc.setColor( Color.RED );
    _gc.drawLine( _line.getTop().getX(), _line.getTop().getY(), _line.getBottom().getX(), _line.getBottom().getY() );
    _gc.setColor( old_color );
  }

}



class LineComponent
{
  private Point _top;    // top point of line component
  private Point _bottom; // bottom pt of line component

  LineComponent()
  {
    _top    = new Point( 0, 0 );
    _bottom = new Point( 0, 0 );
  }

  LineComponent( Point t, Point b )
  {
    _top    = t;
    _bottom = b;
  }

  void setTop( Point top )
  {
    _top = top;
  }

  void setBottom( Point bottom )
  {
    _bottom = bottom;
  }

  Point getTop()
  {
    return _top;
  }

  Point getBottom()
  {
    return _bottom;
  }
}
// end file
Avatar of ozymandias
ozymandias
Flag of United Kingdom of Great Britain and Northern Ireland image

The methods of Point, getX() and getY() return double and you are using them as ints. You will have to cast or convert them or the code will not compile.

Aside from that, you want a JSlider that controls the horizontal position of the intesecting red line on that graph, correct ?
Avatar of fivesigmaevent
fivesigmaevent

ASKER

Hi ozymandias,

> The methods of Point, getX() and getY() return double and you are using them as ints. You will have to cast or convert them or the code will not compile

Oops. :-( Yeah, it's been compiling no problem here. But that's because I forgot that I have a Point.class (wrote it only to discover later on there Java provides a Point class). It has int return types for getX() and getY() in my project directory. Good catch.

> Aside from that, you want a JSlider that controls the horizontal position of the intesecting red line on that graph, correct ?

Yes, that is correct. I figure the line would move with the slider if it was extended as a JSlider, but I'm also not clear if that is the best approach? The graph should be the background. As the line moves horizontally with the slider, it should not obstruct anything painted on the X Y graph.

(Just a side note. Ultimately the graph would display a distribution curve. The app would display some information about where the slider line and curve meet. Somewhat useless, but great practice.)
I would not extend JSlider.

I would write a component that has your existing Plane class and displays a JSLider along the X axis. The component listens to the JSlider for events and when they occur it updates the co-ordinates use to draw the line class and then repaints the graph.
I have modified your code so that it sort of does what you decribe.

I had to mes about with GridbagLayout to get the slider to match the length of the x-axis and even now, the tracking between the line and the pointer gets a bit out at either end of the scale, so it definitely needs some tweaking.

// begin file
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class App extends JFrame implements WindowListener {
      Plane _p;
      private final int _WIDTH = 250;
      private final int _HEIGHT = 400;
      Dimension _plane_size; // size of axis
      Point _offset; // axis' offset from Canvas edges
      LineComponent _line;
      // component that shifts left or right, like a slidebar
      JSlider _slider;

      App() {
            super("WHATEVER DUDE");

            // call so that createImage() and setExtendedState() will work
            //    addNotify();
            // maximize frame
            //    setExtendedState( Frame.MAXIMIZED_BOTH );

            setSize(_WIDTH, _HEIGHT);
            GridBagLayout gbl = new GridBagLayout();
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.NONE;
            gbc.weightx = 0;
            gbc.weighty = 0;
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.insets = new Insets(5,5,5,5);
             
            getContentPane().setLayout(gbl);
            // make a plane and add it to Frame
            _plane_size = new Dimension(200, 200);
            _offset = new Point(5, 5);
            _line = new LineComponent(new Point(100, 30), new Point(100, 250));

            _p = new Plane(_plane_size, _offset, _line);
            gbl.setConstraints(_p,gbc);
            getContentPane().add(_p);
            
            _slider = new JSlider(JSlider.HORIZONTAL,0,215,100);
            _slider.addChangeListener(new ChangeListener(){
                  public void stateChanged(ChangeEvent e) {
                        JSlider source = (JSlider)e.getSource();
                        if (!source.getValueIsAdjusting()) {
                              int xpos = (int)source.getValue();
                              _p.moveLine(xpos);
                              _p.drawLineComponent();
                              _p.repaint();
                        }
                  }            
            });
            gbc.gridy = 1;
            gbc.weightx = 1;
            gbc.insets = new Insets(5,5,5,20);            
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbl.setConstraints(_slider,gbc);
            getContentPane().add(_slider);

            // listen to window events
            addWindowListener(this);

            // show the window
            show();
      }

      void start() {
            _p.drawLineComponent();
            repaint();
      }

      public void windowClosing(WindowEvent e) {
            hide();
            dispose();
      }

      public void windowClosed(WindowEvent e) {
      }

      public void windowOpened(WindowEvent e) {
      }

      public void windowIconified(WindowEvent e) {
      }

      public void windowDeiconified(WindowEvent e) {
      }

      public void windowActivated(WindowEvent e) {
      }

      public void windowDeactivated(WindowEvent e) {
      }

      public static void main(String args[]) {
            // instantiate the app
            App app;
            app = new App();
            app.start();
      }
}

class Plane extends Canvas {
      private Dimension _dimension; // dimensions of plane
      private LineComponent _line;
      // component that shifts left or right, like a slidebar
      private Point _offset; // offset from edges of border
      private Point[] _x_axis;
      // points for X axes; element 0 is left Point, elem 1 is right Point of axis
      private Point[] _y_axis;
      // points for Y axes; element 0 is top Point, elem 1 is bottom Point of axis
      private Image _img; // in-mem image for double buffering draw technique
      private Graphics _gc; // graphics context
      private boolean _first_time; // first time painting
      private final int TOP = 0;
      private final int LEFT = 0;
      private final int RIGHT = 1;
      private final int BOTTOM = 1;

      Plane(Dimension d, Point offset, LineComponent line) {
            // save width, height and offset
            _dimension = d;
            _offset = offset;
            _line = line;

            _first_time = true;
            // setBackground( Color.GRAY );
            setSize(d.width + 30, d.height + 10);

            // called so that createImage() will work
            //    addNotify();

            // allocate space for references
            _y_axis = new Point[2];
            _x_axis = new Point[2];

            // save Points to X and Y axes
            // Y axis points
            _y_axis[0] = new Point((int)_offset.getX(), (int)_offset.getY());
            _y_axis[1] =
                  new Point(
                        (int)_offset.getX(),
                        (int) (_offset.getY() + _dimension.height));

            // X axis points
            _x_axis[0] =
                  new Point(
                        (int)_offset.getX(),
                        (int) (_offset.getY() + _dimension.height));
            _x_axis[1] =
                  new Point(
                        (int) (_offset.getX() + _dimension.width),
                        (int) (_offset.getY() + _dimension.height));

      }

      public void paint(Graphics g) { // use double buffering to draw

            //if (_first_time) { // first time painting, so create an image to draw to
                  // tried to call addNotify() so I could create an Image and Graphics context
                  // prior to this, but to no avail

                  // double buffer to avoid repetitive drawing
                  _img = createImage(350, 350);
                  _gc = _img.getGraphics();
                  _first_time = false;

                  // draw the axes
                  drawAxes();

                  // draws a Line Component
                  drawLineComponent();
            //}

            // _img is the in-mem buffer
            g.drawImage(_img, 0, 0, this);
      }

      // draws an X Y axis
      public void drawAxes() {
            int[] arrow_x;
            int[] arrow_y;

            // space for polygon
            arrow_x = new int[3];
            arrow_y = new int[3];

            // draw Y axis with arrow on top
            _gc.drawLine(
                  (int)_y_axis[0].getX(),
                  (int)_y_axis[0].getY(),
                  (int)_y_axis[1].getX(),
                  (int)_y_axis[1].getY());
            arrow_x[0] = (int)_y_axis[TOP].getX() - 3;
            arrow_x[1] = (int)_y_axis[TOP].getX();
            arrow_x[2] = (int)_y_axis[TOP].getX() + 3;
            arrow_y[0] = (int)_y_axis[TOP].getY() + 5;
            arrow_y[1] = (int)_y_axis[TOP].getY();
            arrow_y[2] = (int)_y_axis[TOP].getY() + 5;
            _gc.drawPolygon(arrow_x, arrow_y, 3);

            // draw X axis with arrow on right
            _gc.drawLine(
                  (int)_x_axis[0].getX(),
                  (int)_x_axis[0].getY(),
                  (int)_x_axis[1].getX(),
                  (int)_x_axis[1].getY());
            arrow_x[0] = (int)_x_axis[RIGHT].getX() - 5;
            arrow_x[1] = (int)_x_axis[RIGHT].getX();
            arrow_x[2] = (int)_x_axis[RIGHT].getX() - 5;
            arrow_y[0] = (int)_x_axis[RIGHT].getY() + 3;
            arrow_y[1] = (int)_x_axis[RIGHT].getY();
            arrow_y[2] = (int)_x_axis[RIGHT].getY() - 3;
            _gc.drawPolygon(arrow_x, arrow_y, 3);

            System.out.println("drawing y and x axes");
      }

      // draws a Line Component
      public void drawLineComponent() {
            Color old_color;
            old_color = _gc.getColor();

            _gc.setColor(Color.RED);
            _gc.drawLine(
                  (int)_line.getTop().getX(),
                  (int)_line.getTop().getY(),
                  (int)_line.getBottom().getX(),
                  (int)_line.getBottom().getY());
            _gc.setColor(old_color);
      }
      
      public void moveLine(int x){
            _line.setTop(new Point(x,(int)_line.getTop().getY()));
            _line.setBottom(new Point(x,(int)_line.getBottom().getY()));            
      }

}

class LineComponent {
      private Point _top; // top point of line component
      private Point _bottom; // bottom pt of line component

      LineComponent() {
            _top = new Point(0, 0);
            _bottom = new Point(0, 0);
      }

      LineComponent(Point t, Point b) {
            _top = t;
            _bottom = b;
      }

      void setTop(Point top) {
            _top = top;
      }

      void setBottom(Point bottom) {
            _bottom = bottom;
      }

      Point getTop() {
            return _top;
      }

      Point getBottom() {
            return _bottom;
      }
}
// end file
ozymandias, I ran your changes. Very cool. Give me some time to look over the code and I will get back to ya.
ozymandias,

First, thanks for your time and patience. Very helpful. If you bear with me for a little longer, I can increase points a little bit more for you since I have some more related questions and Community Service added missing points to my account.

> I have modified your code so that it sort of does what you decribe.

Yes it's very close. However, the line doesn't appear in motion or animated as the slider is dragged and the line itself cannot be dragged with the mouse cursor. Any ideas? Just an abstract explanation of how to would be great.

> I had to mes about with GridbagLayout to get the slider to match the length of the x-axis and even now, the tracking between the line and the pointer gets a bit out at either end of the scale, so it definitely needs some tweaking.

I noticed that too, but no big deal. It is helping me learn at a faster rate.

> I would write a component that has your existing Plane class and displays a JSLider along the X axis. The component listens to the JSlider for events and when they occur it updates the co-ordinates use to draw the line class and then repaints the graph.

OK. I will try that approach, also trying to use some of the ideas you have coded. I take it that a custom component can be composed of multiple components, both pre-existing and custom made, right? Can you recommened some reading, (books and/or websites are fine) that go into depth about creating custom components?

> public void paint( Graphics g ){
    // double buffer to avoid repetitive drawing
    _img = createImage(350, 350);
    _gc = _img.getGraphics();

Hmmm. With the "if" statement gone in paint(), the _img and _gc code need to be pulled out of paint otherwise the in-memory image gets lost with each call to paint(). The "if" statement was there to prevent this. I would rather instantiate _img and _gc in the constructor but would get a null pointer exception when executing. Supposedly addNotify() would cure this by creating the component peer prior to displaying, but did not seem to help.

>>  // draw the axes
  drawAxes();

  // draws a Line Component
  drawLineComponent();

  // _img is the in-mem buffer
  g.drawImage(_img, 0, 0, this);

Seems I should switch the order of g.drawImage() and drawLineComponent() to get the image in the background.

Again, thanks for your assistance.
Ignore my comment about switching the order of g.drawImage() and drawLineComponent(). :-/

I was hoping to store the background image separate from the red line/component so thats why there was a new Image and Graphics context created. The image would be stored such that it would not require redrawing at each paint() invocation. It would then be painted as background and the red line would be layered in the foreground. Holding the actual graph image in memory could avoid potential flicker problems when drawing. I don't know how feasible that is. Thoughts?

Basically, the composite component we are talking about is a "graphical widget". The framework and patterns for creating these was Java Beans.

There are probably lots of books on this.

I will have to think about this a bit more.
ASKER CERTIFIED SOLUTION
Avatar of ozymandias
ozymandias
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
BTW, if you want to see the lines drag properly, comment out the :

       if (!source.getValueIsAdjusting())

statement in each of the ChangeListeners.

This means that the lines will redraw while the slider is being dragged rather than at theh end of the drag.
Wow very cool! I have looked over the code and it will take me a little time to get a solid understanding of the changes. Thanks for all the guidance.

Looks like swing is the way to go for something like this. Figure JPanel would be ideal for making the custom component and I'll do some paint optimization with the paintComponent() methods. I think eventually I have the graph display something like a curve, function or something else and points where the line component intersect will be displayed to labels.
Thanks ozymandias. Appreciate discussing this and getting some ideas and examples from you.

I increased the points to 100 and give you an A.
Thanks. That was interesting :0)
Just for fun I added a few buttons that let you add and remove plot points to the graph using the sliders and join then up into a line graph.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.util.*;

public class SliderGraph extends JFrame{

      Container contentPane;
      JSlider xAxis;
      JSlider yAxis;
      GraphPlane graph;
      LineComponent hLine;
      LineComponent vLine;
      JLabel xyLabel;
      JButton mark;
      JButton unmark;
      JButton clear;

      public SliderGraph(String[] args){
            super("SliderGraph");
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            contentPane = this.getContentPane();
            //contentPane.setLayout(new BorderLayout());

        vLine = new LineComponent(new Point(150, 1), new Point(150, 300));
        hLine = new LineComponent(new Point(1, 150), new Point(300, 150));
        graph = new GraphPlane(new Dimension(300,300),new Point(0,0),vLine,hLine);
            //contentPane.add(graph,BorderLayout.CENTER);
            contentPane.setLayout(null);
            contentPane.add(graph);

            xAxis = new JSlider(JSlider.HORIZONTAL,0,100,50);
        xAxis.setMajorTickSpacing(10);
        xAxis.setMinorTickSpacing(5);
        xAxis.setPaintTicks(true);
        xAxis.setPaintLabels(true);
        xAxis.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
        //contentPane.add(xAxis,BorderLayout.SOUTH);
        contentPane.add(xAxis);
            ChangeListener cl = new ChangeListener(){
                  public void stateChanged(ChangeEvent e) {
                        int ypos = (int)yAxis.getValue();
                        int xpos = (int)xAxis.getValue();
                        xyLabel.setText("x, y = " + xpos + " , " + ypos);
                        if (e.getSource() == xAxis){
                              graph.moveVLine(xpos);
                        }else{
                              graph.moveHLine(ypos);
                        }
                        graph.repaint();
                  }
            };
            xAxis.addChangeListener(cl);

            yAxis = new JSlider(JSlider.VERTICAL,0,100,50);
            yAxis.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        yAxis.setMajorTickSpacing(10);
        yAxis.setMinorTickSpacing(5);
        yAxis.setPaintTicks(true);
        yAxis.setPaintLabels(true);
        yAxis.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
        //contentPane.add(yAxis,BorderLayout.WEST);
        contentPane.add(yAxis);
            yAxis.addChangeListener(cl);


            xyLabel = new JLabel("x, y = 50 , 50");
            mark = new JButton("add");
            unmark = new JButton("remove");
            clear = new JButton("clear");
            contentPane.add(xyLabel);
            contentPane.add(mark);
            contentPane.add(unmark);
            contentPane.add(clear);

            ActionListener al = new ActionListener(){
                  public void actionPerformed(ActionEvent ae){
                        if(ae.getSource() == mark){
                              addPoint();
                        }else if(ae.getSource() == unmark){
                              removePoint();
                        }else if(ae.getSource() == clear){
                              clearPoints();
                        }
                  }
            };

            mark.addActionListener(al);
            unmark.addActionListener(al);
            clear.addActionListener(al);

            yAxis.setBounds(10,2,50,317);
            graph.setBounds(63,10,300,300);
            xAxis.setBounds(53,312,320,50);
            xyLabel.setBounds(55,370,85,20);
            mark.setBounds(145,370,70,20);
            unmark.setBounds(220,370,85,20);
            clear.setBounds(310,370,70,20);
      }

      public void addPoint(){
            graph.addPoint((int)xAxis.getValue(),(int)yAxis.getValue());
            graph.repaint();
      }

      public void removePoint(){
            graph.removePoint((int)xAxis.getValue(),(int)yAxis.getValue());
            graph.repaint();
      }

      public void clearPoints(){
            graph.clearPoints();
            graph.repaint();
      }

      public static void main(String[] args){
            SliderGraph sg = new SliderGraph(args);
            sg.setSize(450,450);
            sg.setVisible(true);
      }


      class GraphPlane extends Canvas {
            private Dimension _dimension; // dimensions of plane
            private LineComponent _hLine;
            private LineComponent _vLine;
            // component that shifts left or right, like a slidebar
            private Point _offset; // offset from edges of border
            private Image _img; // in-mem image for double buffering draw technique
            private Graphics _gc; // graphics context
            private Vector _points;

            GraphPlane(Dimension d, Point offset, LineComponent vLine, LineComponent hLine) {
                  // save width, height and offset
                  _dimension = d;
                  _offset = offset;
                  _hLine = hLine;
                  _vLine = vLine;
                  _points = new Vector();

                  // setBackground( Color.GRAY );
                  setSize(d.width + 0, d.height + 0);
                  // called so that createImage() will work
                  //    addNotify();

            }

            public void addPoint(int x, int y){
                  int xpos = (int)(3.0*x);
                  int ypos = (int)(3.0*(100-y));
                  Point p = new Point(xpos,ypos);
                  if (_points.indexOf(p) == -1){
                        _points.add(p);
                        System.out.println("added point");
                  }else{
                        System.out.println("point exists");
                  }
            }

            public void removePoint(int x, int y){
                  int xpos = (int)(3.0*x);
                  int ypos = (int)(3.0*(100-y));
                  Point p = new Point(xpos,ypos);
                  int i;
                  if ((i = _points.indexOf(p)) == -1){
                        System.out.println("no point exists");
                  }else{
                        _points.removeElementAt(i);
                        System.out.println("removed point");
                  }

            }

            public void clearPoints(){
                  _points = new Vector();
            }

            public void paint(Graphics g) { // use double buffering to draw

                  _img = createImage(300, 300);
                  _gc = _img.getGraphics();
                  drawBorder();
                  plotPoints();
                  drawLineComponents();
                  g.drawImage(_img, 0, 0, this);
            }

            public void plotPoints(){
                  Point last = new Point();
                  for (int i = 0; i < _points.size(); i++){
                        Point p = (Point)_points.elementAt(i);
                        _gc.drawRect((int)p.getX()-1,(int)p.getY()-1,3,3);
                        if (i > 0){
                              _gc.drawLine((int)last.getX(),(int)last.getY(),(int)p.getX(),(int)p.getY());
                        }
                        last = p;
                  }
            }

            public void drawBorder(){
                  _gc.drawLine(0,0,0,299);
                  _gc.drawLine(0,0,299,0);
                  _gc.drawLine(299,0,299,299);
                  _gc.drawLine(0,299,299,299);
            }

            // draws a Line Component
            public void drawLineComponents() {
                  Color old_color;
                  old_color = _gc.getColor();

                  _gc.setColor(Color.RED);
                  _gc.drawLine(
                        (int)_vLine.getTop().getX(),
                        (int)_vLine.getTop().getY(),
                        (int)_vLine.getBottom().getX(),
                        (int)_vLine.getBottom().getY());

                  _gc.setColor(Color.GREEN);
                  _gc.drawLine(
                        (int)_hLine.getTop().getX(),
                        (int)_hLine.getTop().getY(),
                        (int)_hLine.getBottom().getX(),
                        (int)_hLine.getBottom().getY());

                  _gc.setColor(old_color);
            }

            public void moveVLine(int x){
                  int xpos = (int)(3.0*x);
                  _vLine.setTop(new Point(xpos,(int)_vLine.getTop().getY()));
                  _vLine.setBottom(new Point(xpos,(int)_vLine.getBottom().getY()));
            }

            public void moveHLine(int y){
                  int ypos = (int)(3.0*(100-y));
                  _hLine.setTop(new Point((int)_hLine.getTop().getX(),ypos));
                  _hLine.setBottom(new Point((int)_hLine.getBottom().getX(),ypos));
            }

      }

      class LineComponent {
            private Point _top; // top point of line component
            private Point _bottom; // bottom pt of line component

            LineComponent() {
                  _top = new Point(0, 0);
                  _bottom = new Point(0, 0);
            }

            LineComponent(Point t, Point b) {
                  _top = t;
                  _bottom = b;
            }

            void setTop(Point top) {
                  _top = top;
            }

            void setBottom(Point bottom) {
                  _bottom = bottom;
            }

            Point getTop() {
                  return _top;
            }

            Point getBottom() {
                  return _bottom;
            }
      }
}
hey ozymandias,

Very neat stuff! I will play around with it more tonight. Will try to add some stuff to it.

Off Topic: Do you happen to know how to draw state machine diagrams in Java or know of any libraries that can be used to do that? If you do, I can start another question thread and address it to you.



Not sure what a state machine diagram is.
I use visio for my diagrams stuff like state transition, flow control, UML etc.

There probably is a java package for doing it, but I;ve not come across one.
ozymandias,

I see what you are talking about with respect to the composite widget. It uses the model/state and delegate design patterns. Hopefully I will be able to develop without too much struggle.

A state machine diagram is very similar to what you would create with Visio, with a start state, directed edges to other states based on input, and final or acceptance states. I will try a web search for a package that does this, as it is a very complicated task to draw nice state machines.