Member Avatar for v3ga

I just started learning Java (Currently, I know basic swing components like JPanels, text areas, textfields etc) and I have chosen a Logic Circuit Simulator as a project. My objective is to simulate the working of logic gates by making circuits using drag & drop. So far, I have thought about putting the devices in a JList and then making a JPanel for the circuit area. The users will drag out of the JList and onto the JPanel where the circuit will be made. But I have no idea how to implement this in java. Please give me some directions as to how to go about with this.

Recommended Answers

All 4 Replies

I built a lttle training demo on exactly this theme a while back, and it's not easy for someone who has "just started learning Java". You're going to have to build some specific skills and cod techniques first before you try to put them all together into this app. I suggest you start by building an app that has a window with a simple square drawn on it that you can drag around with the mouse. If you want to take this kind of step-by-step approach I'm happy to guide you through it.

Member Avatar for v3ga

By "just started learning java", I mean I have 2 months experience on this and around a month on GUI like JPanel,JFrame etc. I really do hope that I would be able to build this app. As for the one you mentioned, I have done something very similar to that for a Convex Hull finding app where the user can click to make small squares and can also drag them around by clicking inside them and dragging. I did this by implementing the MouseListener and MouseMotionListener.
But in this app, I want to move from one JList to a JPanel and I don't want to simply paint the image but an object of the class of the corresponding device should be made(atleast that's what I think). I think what I need is java.awt.dnd(based on a nice article at http://www.javaworld.com/javaworld/jw-03-1999/jw-03-dragndrop.html) Can you point me to some example code or something.

Here's the code I used in a tutorial a while back to illustrate some of the techniques for exactly this kind of app. The lecture "notes" were all in my head, so the code is all there is. I'm posting it here in case it turns out to be useful for you. Don't try to use it directly, but you may find the coneceptsd it employs useful in your own design. I'm happy to answer any questions.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.LineBorder;

/* Runnable demo of Java techniques for an electrical circuit design application
 * has NAND gates with connections etc and a drag'n'drop GUI
 * Main classes:
 * Component - abstract class for gates etc. Has inputs & outputs and a drawing method 
 *   NANDGate - concrete Component - you can create others for NOR etc
 * Connector - abstract class for inputs and outputs
 *   Input, Output - concrete Connectors
 * Connection - joins two Connectors (joins one Input to one Output)
 * 
 * These classes have the logic for GUI interaction (dragging, selecting Connectors etc)
 * but also have the potential for simulating the actual circuit behaviour.
 * 
 */

public class CircuitDesigner {

 // NAND gate drag'n'drop demo with connections etc

 JFrame frame = new JFrame("drag demo");

 ArrayList<Component> components = new ArrayList<Component>();
 ArrayList<Connection> lines = new ArrayList<Connection>();

 JLabel prompt = new JLabel("Welcome. Add a component to start");
 DrawPanel drawPanel = new DrawPanel();

 boolean showInputs = true, showOutputs = true;
 boolean addingComponent = false, selectingOutput = false, selectingInput = false;
 Output selectedOutput = null;

 CircuitDesigner() {

    frame.setMinimumSize(new Dimension(500, 400));
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    frame.add(prompt, BorderLayout.PAGE_START);

    drawPanel.setLayout(null);
    frame.add(drawPanel);

    JPanel buttonPanel = new JPanel();
    frame.add(buttonPanel, BorderLayout.PAGE_END);
    JButton newComponentButton = new JButton("Add a Component");
    buttonPanel.add(newComponentButton);
    newComponentButton.addActionListener(new ActionListener() {

       @Override
       public void actionPerformed(ActionEvent arg0) {
          addNewComponent();
       }
    });
    JButton newConnectionButton = new JButton("Add a Connection");
    buttonPanel.add(newConnectionButton);
    newConnectionButton.addActionListener(new ActionListener() {

       @Override
       public void actionPerformed(ActionEvent arg0) {
          addNewConnection();
       }
    });

    frame.setVisible(true);
 }

 void addNewComponent() {
    prompt.setText("Click to add component");
    addingComponent = true;
 }

 void addNewConnection() {
    prompt.setText("Click an output to connect...");
    selectingOutput = true;
    showInputs = false;
    drawPanel.repaint();
 }

 abstract class Component extends JLabel implements MouseListener, MouseMotionListener {

    // A component is a movable draggable object with inputs and outputs

    ArrayList<Input> inputs = new ArrayList<Input>();
    ArrayList<Output> outputs = new ArrayList<Output>();

    public Component(String text, int x, int y) {
       super(text);
       addMouseListener(this);
       addMouseMotionListener(this);
    }

    public void addOutput(Output o) {
       outputs.add(o);
    }

    public void addInput(Input i) {
       inputs.add(i);
    }

    @Override
    public void paintComponent(Graphics g) {
       // can do custom drawing here, but just show JLabel text for now...
       super.paintComponent(g);
       Graphics2D g2d = (Graphics2D) g;
       if (showInputs) {
          for (Input in : inputs) {
             in.paintConnector(g2d);
          }
       }
       if (showOutputs) {
          for (Output out : outputs) {
             if (out.isAvailable()) out.paintConnector(g2d);
          }
       }
    }

    int startDragX, startDragY;
    boolean inDrag = false;

    @Override
    public void mouseEntered(MouseEvent e) {
       // not interested
    }

    @Override
    public void mouseExited(MouseEvent e) {
       // not interested
    }

    @Override
    public void mousePressed(MouseEvent e) {
       startDragX = e.getX();
       startDragY = e.getY();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
       if (inDrag) {
          System.out.println("\"" + getText().trim() + "\" dragged to " + getX() + ", "
                + getY());
          inDrag = false;
       }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
       if (selectingInput) {
          for (Input in : inputs) {
             if (in.isAvailable() && in.contains(e.getPoint())) {
                System.out.println("Click on input connector of \"" + getText().trim()
                      + "\"");
                lines.add(new Connection(selectedOutput, in));
                drawPanel.repaint();
                selectingInput = false;
                showOutputs = true;
                prompt.setText(" ");
             }
          }
       }
       if (selectingOutput) {
          for (Output out : outputs) {
             if (out.isAvailable() && out.contains(e.getPoint())) {
                System.out.println("Click on output connector of \"" + getText().trim()
                      + "\"");
                selectedOutput = out;
                selectingOutput = false;
                showOutputs = false;
                selectingInput = true;
                showInputs = true;
                prompt.setText("Click an input to connect...");
                drawPanel.repaint();
                return;
             }
          }
       }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
       int newX = getX() + (e.getX() - startDragX);
       int newY = getY() + (e.getY() - startDragY);
       setLocation(newX, newY);
       inDrag = true;
       frame.repaint();
    }

    @Override
    public void mouseMoved(MouseEvent arg0) {
       // not interested
    }

 }

 class NANDgate extends Component {

    NANDgate(int x, int y) {
       super("    NAND " + (components.size() + 1), x, y);

       setVerticalAlignment(SwingConstants.CENTER);
       setBounds(x, y, 70, 30);
       setBorder(new LineBorder(Color.black, 1));

       new Input(this, 0, 6);
       new Input(this, 0, getHeight() - 6);
       new Output(this, getWidth(), getHeight() / 2);
    }
 }

 abstract class Connector { // subclasses: Input, Output

    Component component;
    ArrayList<Connection> connections = new ArrayList<Connection>();
    int maxConnections;

    int x, y; // x,y is the point where lines connect
    int w = 10, h = 10; // overall width & height of Connector
    Shape shape;
    Color color;

    Connector(Component c, int x, int y) {
       this.component = c;
       this.x = x;
       this.y = y;
    }

    boolean isAvailable() {
       // check that max fanout/fanin will not be exceeded
       return connections.size() < maxConnections;
    }

    void addConnection(Connection con) {
       if (isAvailable()) connections.add(con);
       // may want to show error somehow if not available?
    }

    public int getX() {
       return component.getX() + x;
    }

    public int getY() {
       return component.getY() + y;
    }

    public boolean contains(Point p) {
       // used to check for mouse clicks on this Connector
       return shape.contains(p);
    }

    public void paintConnector(Graphics2D g2d) {
       if (isAvailable()) g2d.setColor(color);
       else g2d.setColor(Color.lightGray);
       g2d.fill(shape);
    }

 }

 class Output extends Connector {

    // Triangle, left facing, at RHS of component (points away from component)

    Output(Component owner, int x, int y) {
       super(owner, x, y);
       owner.addOutput(this);
       maxConnections = 4; // fanout for output
       x = owner.getWidth(); // RHS of Component
       y = owner.getHeight() / 2;
       Polygon p = new Polygon();
       p.addPoint(x - w, y - h / 2);
       p.addPoint(x - w, y + h / 2);
       p.addPoint(x, y);
       shape = p;
       color = Color.red;
    }

 }

 class Input extends Connector {

    // Triangle, left facing, at LHS of component (points into the component)

    Input(Component owner, int x, int y) {
       super(owner, x, y);
       owner.addInput(this);
       maxConnections = 1; // only one connection to an input
       Polygon p = new Polygon();
       p.addPoint(x, y - h / 2);
       p.addPoint(x, y + h / 2);
       p.addPoint(x + w, y);
       shape = p;
       color = Color.blue;
    }

 }

 class Connection {

    // a Connection connects an Output to an Input.

    Output output;
    Input input;

    public Connection(Output output, Input input) {
       this.output = output;
       this.input = input;
       output.addConnection(this);
       input.addConnection(this);
    }

    public void paintConnection(Graphics2D g2d) {
       g2d.drawLine(output.getX(), output.getY(), input.getX(), input.getY());
    }

 }

 class DrawPanel extends JPanel {

    // contains Components, draws lines (connecting pairs of components)

    DrawPanel() {
       addMouseListener(new MouseAdapter() {

          @Override
          public void mouseClicked(MouseEvent e) {
             if (addingComponent) {
                Component c = new NANDgate(e.getX(), e.getY());
                components.add(c);
                drawPanel.add(c);
                addingComponent = false;
                prompt.setText("Use mouse to drag components");
                drawPanel.repaint();
             }
          }
       });
    }

    @Override
    public void paintComponent(Graphics g) {
       super.paintComponent(g);
       Graphics2D g2d = (Graphics2D) g;
       g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
             RenderingHints.VALUE_ANTIALIAS_ON);
       for (Connection line : lines) {
          line.paintConnection(g2d);
       }
    }


 }

 public static void main(String[] args) {
       new CircuitDesigner();
    }

}
commented: Thankyou. This way is much easier than using java.awt.dnd +2
Member Avatar for v3ga

My application is complete. Thanks a lot for your help

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.