Lyra
Lyra

Reputation: 19

Trying to get one mouse listener action to stop before another one begins

Not sure how well I will explain this; I'm quite new to programming...

So I'm trying to make a desktop application that draws musical notes of a given type on some sheet music when the user selects the button corresponding to that type of note. Currently, if the user selects the "Whole Note" button, the user can then start clicking on the screen and the note will be drawn where the click occurred. It will also make a "ding" sound and write some info about that note to a text file.

That's all well and good, but unfortunately when the user selects a new button, say the "Quarter Note" button, for each mouse click there will be two notes drawn (one whole, one quarter), two dings, and two packets of info written to the file. I have no idea how to make this work! Currently, I'm trying to use threads, such that each button creates a new thread and the thread currently in use is interrupted when a new button is pressed, but that doesn't resolve the issue.

Initially, an empty linked list of threads ("NoteThreads") is constructed. Also, a private class known as SheetMusicPane (given the variable name "smp") is constructed in order to draw the sheet music. The buttons are added in the main constructor (public CompositionStudio), whereas the method containing the mouse listener (see what follows) is contained in the SheetMusicPane private class. Not sure whether that is part of the problem.

I have a button action listener:

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        if (!threads.isEmpty()) {
            NoteThread oldThread = threads.remove();
            oldThread.interrupt();
        }
        NoteThread newThread = new NoteThread(e.getActionCommand());
        threads.add(newThread);
        newThread.run();
    }
});

that produces a thread:

private class NoteThread extends Thread {

    private String note;

    public NoteThread(String note) {
        this.note = note;
    }

    public void run() {
        smp.getShape(smp.getGraphics(), note);
    }
}

that when, on running, calls this method with graphics and a mouse listener:

public void getShape(final Graphics g, final String note) {
    this.addMouseListener(new MouseListener() {
        public void mousePressed(MouseEvent e) {
        }
        public void mouseReleased(MouseEvent e) {
        }
        public void mouseEntered(MouseEvent e) {
        }
        public void mouseExited(MouseEvent e) {
        }
        public void mouseClicked(MouseEvent e) {
           Point p = MouseInfo.getPointerInfo().getLocation();
           addShape(g, p.x, p.y, note);
           int pitch = 12;
           piano.playNote(pitch);
           advance(1.0, piano);
           try { addToFile(pitch, note);}
           catch(FileNotFoundException fnfe) {}
           catch(IOException ioe) {}
        }
     });
  }

The above method is responsible for drawing the note ("addShape()"), making the ding sound, and writing to the file.

Thanks in advance for any help you can give!

Upvotes: 0

Views: 144

Answers (2)

Brian S
Brian S

Reputation: 5056

One solution would be to simply fetch all the listeners (which should be 1) and remove them before adding the new listener:

public void getShape(final Graphics g, final String note) {
    MouseListener[] listeners = this.getMouseListeners();
    for (MouseListener ml : listeners) {
        this.removeMouseListener(ml);
    }
    this.addMouseListener(new MouseListener()...);
}

An alternative, since you have a finite number of buttons, would be to create a finite set of listeners, eg:

private MouseListener wholeNote = new MouseListener()...;
private MouseListener quarterNote = new MouseListener()...;

Create a reference to the "current" listener (private MouseListener current;), and have a means of deciding which listener to use whenever getShape is called (a series of if conditions on the note String would work, although I would prefer some refactoring to use an enum personally). Then you could do something along the lines of:

private MouseListener wholeNote = new MouseListener()...;
private MouseListener quarterNote = new MouseListener()...;
private MouseListener current;

...

public void getShape(final Graphics g, final String note) {
    if (current != null) {
        this.removeMouseListener(current);
    }

    if (...) { // note is Whole Note
        current = wholeNote;
    } else if (...) { // note is Quarter Note
        current = quarterNote;
    } // etc.

    this.addMouseListener(current);
}

Another alternative would be to change your listener so that you only ever need the one, but clicking a button changes a variable which the listener has access to. For example:

// In the listener
    public void mouseClicked(MouseEvent e) {
       Point p = MouseInfo.getPointerInfo().getLocation();
       addShape(g, p.x, p.y, currentNote);
       int pitch = 12;
       piano.playNote(pitch);
       advance(1.0, piano);
       try { addToFile(pitch, currentNote);}
       catch(FileNotFoundException fnfe) {}
       catch(IOException ioe) {}
    }

// In the same class
protected String currentNote;

...

public void getShape(final Graphics g, final String note) {
    currentNote = note;
}

Upvotes: 0

Silly Freak
Silly Freak

Reputation: 4231

what you're trying to do does not require multithreading. This is the approach that I'd take:

  • set up a set of toggle buttons or radio buttons to select the note to paint. this way, only one note will be selected at a time. add action listeners to those that store in an adequately scoped variable what note is selected, or infer that every time a note should be drawn. this way, you don't even add action listeners to the buttons. in any case, don't spawn new threads.
  • in your mouse listener, find out what note to draw, and do that - only one note.

if you can, stay away from multithreading, especially as a beginner. also, I think you confuse adding and running listeners here. each call to getShape() adds a new listener, meaning they accumulate over time, which might be the cause of your problems.

PS: welcome to stackoverflow! your question contained the important information and I could infer that you tried solving the problem yourself. It's pleasant to answer such questions!

Upvotes: 1

Related Questions