1

EDIT--CORRECTED CODE BASED ON ACCEPTED ANSWER IS SHOWN AT BOTTOM OF THIS POST.

I found a way (written by Andremoniy) to catch double- and triple-clicks without firing single- and double-clicks. It works very well for my purposes.

I modified it so that it could have the click interval tweaked by the implementation. That also works well.

My modifications include making it an abstract class, defining the 5 abstract methods that the implementation would have to define (methods for single, double, triple, and "many" clicks as well as the click interval tweaker).

Here is the modified version (the println statements are instructive):

public abstract class Click123 extends JPanel  
{
  public abstract void singleClick();
  public abstract void doubleClick();
  public abstract void tripleClick();  
  public abstract void manyClick();
  public abstract int  getFreq(); 

  public Click123()
  {
    addMouseListener
    (
      new MouseAdapter() 
      {
        Thread cp = null;

        public void mouseClicked(MouseEvent e) 
        {
          if (cp != null && cp.isAlive())
            cp.interrupt(); 

          if (e.getClickCount() == 1) 
          {
            cp =  new Thread(new ClickProcessor(new Callable<Void>() {
              @Override public Void call() throws Exception {
                singleClick();
                return null;
              }
            }));
            cp.start();
          }
          else if (e.getClickCount() == 2) 
          {
            cp = new Thread(new ClickProcessor(new Callable<Void>() {
              @Override public Void call() throws Exception {
                doubleClick();
                return null;
              }
            }));
            cp.start();
          }
          else if (e.getClickCount() == 3) 
          {
            cp =  new Thread(new ClickProcessor(new Callable<Void>() 
            {
              @Override public Void call() throws Exception {
                tripleClick();
                return null;
              }
              })              
            );
            cp.start();
          }
          else manyClick();
        } // mouseClicked
      }  // new MouseAdapter
    ); // add mouseListener
  } // Click123  

  class ClickProcessor implements Runnable 
  {
    Callable<Void> eventProcessor;

    ClickProcessor(Callable<Void> eventProcessor) 
    {
        this.eventProcessor = eventProcessor;
    }

    @Override public void run() 
    {
      try 
      {
          Thread.sleep(getFreq());
          eventProcessor.call();
      } catch (InterruptedException e) { System.out.println(e);} 
        catch (Exception e)            { System.out.println(e);}
    }  // run
  }  // class ClickProcessor
} // class Click123

Here's the implementing program:

public class NewMain1 {
  static int INITIAL_CLICK_FREQUENCY = ((Integer)Toolkit.getDefaultToolkit()
      .getDesktopProperty("awt.multiClickInterval"));

  public static int CLICK_FREQUENCY;

  static       JSpinner spinner   = new JSpinner();
  static final JLabel   ch        = new JLabel("Click here to test");

  public static void main(String[] args) {

    CLICK_FREQUENCY = INITIAL_CLICK_FREQUENCY;
    spinner = new JSpinner();
    spinner.setModel(new SpinnerNumberModel(CLICK_FREQUENCY, 200, 900, 50));
    spinner.addChangeListener(new ChangeListener() {
      @Override
      public void stateChanged(ChangeEvent e) {
        CLICK_FREQUENCY = (int) spinner.getValue();
      }
    });

    Click123 frame = new Click123(){ 
      public void singleClick(){
        JOptionPane.showMessageDialog(null,"Single click at " + CLICK_FREQUENCY);
      }
      public void doubleClick(){
        JOptionPane.showMessageDialog(null,"Double click at " + CLICK_FREQUENCY);
      }
      public void tripleClick(){
        JOptionPane.showMessageDialog(null,"Triple click at " + CLICK_FREQUENCY);
      }
      public void manyClick(){
        JOptionPane.showMessageDialog(null,"Many clicks at " + CLICK_FREQUENCY);  
      }
      public int getFreq(){
        return CLICK_FREQUENCY; 
      }
    };
    frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    frame.setLayout(new FlowLayout());
    frame.setSize(500, 300);
              ch.setBorder(new EtchedBorder());
    frame.add(ch);
    frame.add(spinner);
    frame.setVisible(true);
  } // main
}

As originally written, Click123 extends JFrame. I modified it (by changing the top line to extends JTextField) to work with a grid of type JTextField. Then I added a button to change the click interval on the fly. So now I have THREE identical (except for the extension type) huge hunks of code.

My question is this: How do I modify Click123 so that, without alteration, it can be used to make ANY component aware of single-, double-, and triple-clicks?

EDIT--SUMMARY OF NECESSARY CHANGES (For my own app, the abstract methods need MouseEvent to be passed in order to determine which component fired the clicks):

Class definition:

 public abstract class Click123<T extends Component>  
 {
   public abstract void singleClick(MouseEvent e); // same for 3 others
    ...

    public Click123(T target)
    {
      target.addMouseListener
      (
        new MouseAdapter() ...
     } ...
  }

final added as modifier in order to pass MouseEvent:

    public void mouseClicked(final MouseEvent e) 

Pass MouseEvent:

    singleClick(e); // same for doubleClick and tripleClick and manyClick

Receive MouseEvent in order to determine which component fired the clicks:

  public void doubleClick(MouseEvent e)

Implementation:

frame = new JPanel();
new Click123(frame) {
Community
  • 1
  • 1
DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • Hi, sorry, but which part of your original code needs to be taken out in order to add the solution you added? Thanks. – Unheilig Feb 26 '15 at 09:45

3 Answers3

3

Generics aren't necessary. All you need is to add the MouseListener to a Component.

public abstract class Click123 // doesn't extend anything
{
    public Click123(Component comp)
    {
        comp.addMouseListener(new MouseAdapter() {
           // ...
        }));
    }
}

Now you can just pass the JFrame, or whatever Component you want:

Click123 handler = new Click123(myJFrame){ 
  public void singleClick(){
    JOptionPane.showMessageDialog(null,"Single click at " + CLICK_FREQUENCY);
  }
  // Other implementing methods here
}
rgettman
  • 176,041
  • 30
  • 275
  • 357
  • @rgettman--I only found your answer after spending 30 minutes fixing my code after seeing the answer from k_g below. I'm going to investigate your simpler solution later. I'm just happy I got the sucker to work! – DSlomer64 Feb 26 '15 at 00:16
1

Generics are part of the solution. Just do the following:

  1. Make your class Click123<T extends Component>
  2. Make your constructor public Click123(T targetedComponent) {
  3. Call targetedComponent.addMouseListener rather than addMouseListener
  4. Create a JFrame or JPanel or whatever and then pass it to your new Click123 as a constructor argument. Then use your JFrame where you were using your Click123.
k_g
  • 4,333
  • 2
  • 25
  • 40
  • THANK YOU, @k_g! I found your answer within 1 minute of you posting it, but it took me 30 minutes to implement because in my frustration I had thrown away all the correct argument lists for the abstract methods and didn't realize it right away. I was pretty close, yes? I will edit my originall post to represent actual working code. – DSlomer64 Feb 26 '15 at 00:13
  • @DSlomer64 You're welcome and yes, you were pretty close. By the way, don't edit your original post to include working code as that will make it more difficult for future viewers of the site to understand what is going on. The edit will likely be rolled back. – k_g Feb 26 '15 at 00:16
  • thanks. I thought editing original post was preferable. I'll post my corrected code as an answer, then, yes? – DSlomer64 Feb 26 '15 at 00:17
  • @DSlomer64 No, that isn't necessary either. Anyone should be able to combine your question with my answer to get the correct code. – k_g Feb 26 '15 at 00:20
  • I "compromised" before reading your last comment, thinking that I needed to do SOMEthing. The edits are a one-liner at the start and a small contiguous hunk at the end. If they need to go, it'll be easy. – DSlomer64 Feb 26 '15 at 00:46
0

Upon request, I am posting code that works. Edits in original post are too scattered to be useful. Original code modifications are flagged with //////////.

package clickForm;

import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.concurrent.Callable;    

public abstract class Click123<T extends Component>  ////////////////
{
  public abstract void singleClick(MouseEvent e); ////////////////
  public abstract void doubleClick(MouseEvent e); ////////////////
  public abstract void tripleClick(MouseEvent e); ////////////////
  public abstract void manyClick(MouseEvent e);   ////////////////
  public abstract int  getFreq(); 

  public Click123(T target)  ////////////////
  {
    target.addMouseListener  ////////////////
    (
      new MouseAdapter()   ////////////////
      {
        Thread cp = null;

        public void mouseClicked(final MouseEvent e) 
        {
          if (cp != null && cp.isAlive())
            cp.interrupt(); 

          if (e.getClickCount() == 1) 
          {
            cp =  new Thread(new ClickProcessor(new Callable<Void>() {
              @Override public Void call() throws Exception {
                singleClick(e); //////////////////////////////////////////
                return null;
              }
            }));
            cp.start();
          }
          else if (e.getClickCount() == 2) 
          {
            cp = new Thread(new ClickProcessor(new Callable<Void>() {
              @Override public Void call() throws Exception {
                doubleClick(e); //////////////////////////////////////////
                return null;
              }
            }));
            cp.start();
          }
          else if (e.getClickCount() == 3) 
          {
            cp =  new Thread(new ClickProcessor(new Callable<Void>() 
            {
              @Override public Void call() throws Exception {
                tripleClick(e); //////////////////////////////////////////
                return null;
              }
              })              
            );
            cp.start();
          }
          else manyClick(e); //////////////////////////////////////////
        } // mouseClicked
      }  // new MouseAdapter
    ); // add mouseListener
  } // Click123  

  class ClickProcessor implements Runnable 
  {
    Callable<Void> eventProcessor;

    ClickProcessor(Callable<Void> eventProcessor) 
    {
        this.eventProcessor = eventProcessor;
    }

    @Override public void run() 
    {
      try 
      {
          Thread.sleep(getFreq());
          eventProcessor.call();
      } catch (InterruptedException e) { System.out.println(e);} 
        catch (Exception e)            { System.out.println(e);}
    }  // run
  }  // class ClickProcessor
} // class Click123

Implementation:

package clickForm;

import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;   

public class Main {

static int CLICK_FREQUENCY = (int) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");

  static       JSpinner spinner   = new JSpinner();
  static final JLabel   ch        = new JLabel("Click here to test");
  static       JFrame   frame     = new JFrame();

  public static void main(String[] args) {

    spinner = new JSpinner();
    spinner.setModel(new SpinnerNumberModel(CLICK_FREQUENCY, 200, 900, 50));
    spinner.addChangeListener(new ChangeListener() {
      @Override
      public void stateChanged(ChangeEvent e) {
        CLICK_FREQUENCY = (int) spinner.getValue();
      }
    });

    new Click123(ch){   /////////////////////////////////////////////

      @Override
      public void singleClick(MouseEvent e) { //////////////////////////////
        JOptionPane.showMessageDialog(null,"Single at " + CLICK_FREQUENCY);
      }

      @Override
      public void doubleClick(MouseEvent e) { //////////////////////////////
        JOptionPane.showMessageDialog(null,"Double at " + CLICK_FREQUENCY);
      }

      @Override
      public void tripleClick(MouseEvent e) { //////////////////////////////
         JOptionPane.showMessageDialog(null,"Triple at " + CLICK_FREQUENCY);
      }

      @Override
      public void manyClick(MouseEvent e) { //////////////////////////////
        JOptionPane.showMessageDialog(null,"Many at " + CLICK_FREQUENCY);  
      }

      @Override
      public int getFreq() {
        return CLICK_FREQUENCY;
      }
    };
    frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    frame.setLayout(new FlowLayout());
    frame.setSize(500, 300);
              ch.setBorder(new EtchedBorder());
    frame.add(ch);
    frame.add(spinner);
    frame.setVisible(true);
  } // main
}
DSlomer64
  • 4,234
  • 4
  • 53
  • 88