view src/uk/ac/qmul/eecs/depic/jhapticgui/examples/XYPad.java @ 1:46671fc7d649 tip

fixed "window" message bug and brought the message outside the haptic device monitor
author Fiore Martin <f.martin@qmul.ac.uk>
date Fri, 13 Mar 2015 13:02:16 +0000
parents 011caca7515a
children
line wrap: on
line source
/*
XYPad - a haptic xy-pad that uses the jHapticGUI library

Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package uk.ac.qmul.eecs.depic.jhapticgui.examples;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.InputStream;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.text.DefaultCaret;

import uk.ac.qmul.eecs.depic.jhapticgui.HapticDevice;
import uk.ac.qmul.eecs.depic.jhapticgui.HapticDeviceWinFactory;
import uk.ac.qmul.eecs.depic.jhapticgui.HapticListener;
import uk.ac.qmul.eecs.depic.jhapticgui.HapticsException;

/**
 * A haptic XYPad. 
 * 
 * Provides two buttons for starting the haptic device and change 
 * the shape of the haptic pad, a text area to display notification messages 
 * (also from the haptic device) and an XYPanel displaying a cursor that 
 * can be manipulated using the haptic device.  
 *  
 * 
 * @author Fiore Martin
 *
 */
public class XYPad extends JFrame implements ActionListener{
	private static final long serialVersionUID = 1L;
	private XYPanel xyPanel;
	private JButton startButton;
	private JButton changeButton;
	private HapticDevice hapticDevice;
	private JTextArea textArea;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		try {
            // Set System L&F
        UIManager.setLookAndFeel(
            UIManager.getSystemLookAndFeelClassName());
		}catch(Exception e){}	 
		
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					XYPad frame = new XYPad();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Creates the frame.
	 */
	public XYPad() {
		super("XY Pad");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 542);
		JPanel contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		xyPanel = new XYPanel();
		xyPanel.setBackground(Color.LIGHT_GRAY);
		xyPanel.setBorder(new LineBorder(Color.GRAY));
		xyPanel.setBackground(Color.BLACK);
		contentPane.add(xyPanel, BorderLayout.CENTER);
		
		JPanel buttonsPanel = new JPanel();
		contentPane.add(buttonsPanel, BorderLayout.SOUTH);
		buttonsPanel.setLayout(new BorderLayout(0, 0));
		
		JPanel panel_1 = new JPanel();
		buttonsPanel.add(panel_1, BorderLayout.SOUTH);
		buttonsPanel.setPreferredSize(new Dimension(buttonsPanel.getWidth(), 100));
		
		startButton = new JButton("Start Haptics");
		panel_1.add(startButton);
		
		changeButton = new JButton("Change to Square");
		panel_1.add(changeButton);
		changeButton.setEnabled(false);
		
		textArea = new JTextArea();
		/* this makes the scroll pane to always be at the bottom */
		((DefaultCaret) textArea.getCaret()).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
		textArea.setEditable(false);
		buttonsPanel.add(new JScrollPane(textArea), BorderLayout.CENTER);
		
		/* install listener to buttons */
		startButton.addActionListener(this);
		changeButton.addActionListener(this);
	}

	/**
	 * The action performed by the two buttons of this XYPad. 
	 * 
	 * The first button starts the haptic device, whereas the second button 
	 * sends a message to the haptic device to change the shape of the haptic 
	 * xy pad shape.  
	 * 
	 */
	@Override
	public void actionPerformed(ActionEvent evt) {
		if(evt.getSource().equals(startButton)){
			
			textArea.append("Starting haptic device...\n");
			
			/**
			 * creates a new haptic listener that updates the xy panel upon receiving "position" 
			 * messages. Moreover it prints on the text area when the haptic proxy touches
			 * and untouches the haptic xy pad    
			 * 
			 *
			 */
			class XYHapticListener extends HapticListener {
				
				@Override
				public void messageReceived(String command, final String args, int id) {
					
					switch(command){
						case "position" : {
							
							/* The coordinates are passed as a string representing two floats (x and y). *
							 * so the string needs to be unpacked and parsed.  						     */
							String[] coord = args.split(" ");
							final float x = Float.parseFloat(coord[0]);
							final float y = Float.parseFloat(coord[1]);
							
							/* The haptic listener runs on its own thread, therefore changes *  
							 * to the GUI must be queued for the event dispatching thread.   */
							SwingUtilities.invokeLater(new Runnable(){
								@Override
								public void run(){
									xyPanel.setXY(x, y);
								}
							});
						} break;
						
						case "touch" : {
							/* The haptic listener runs on its own thread, therefore changes *  
							 * to the GUI must be queued for the event dispatching thread.   */
							SwingUtilities.invokeLater(new Runnable(){
								@Override
								public void run(){
									 
									/* uses message argument to discriminate between touch and untouch */
									if("yes".equals(args)){
										textArea.append("haptic xy touched\n");
									}else if("no".equals(args)){
										textArea.append("haptic xy untouched\n");
									}
								}
							});
						} break;
						
						case HapticListener.WINDOW_MSG : {
							SwingUtilities.invokeLater(new Runnable(){
								@Override
								public void run(){
									requestFocus();
								}
							});
						} break;
					}
				}
			};
			
			InputStream  dll = this.getClass().getResourceAsStream("XYPad.dll");
			try {
				hapticDevice = new HapticDeviceWinFactory().getHapticDevice(dll, new XYHapticListener(), new Dimension (getWidth(), getWidth() ));
				
				changeButton.setEnabled(true);
				startButton.setEnabled(false);
				
				textArea.append("haptic device started\n");
			} catch (HapticsException e) {
				e.printStackTrace();
				textArea.append(e.getMessage());
			}
			
		}else if(evt.getSource().equals(changeButton)){
			/* Changes the shape of the haptics object  use message args to tell      *
			 * the haptics which shape to display ID is left to 0 as there is         *
			 * only one haptic shape and no need to refer to a specific haptic shape. */ 
			if("Change to Square".equals(changeButton.getText())){
				changeButton.setText("Change to Circle");
				
				hapticDevice.sendMessage("change", "square", 0);
				
				textArea.append("shape changed to square\n");
			}else if("Change to Circle".equals(changeButton.getText())){ // change to circle
				changeButton.setText("Change to Square");
				
				hapticDevice.sendMessage("change", "circle", 0);
				
				textArea.append("shape changed to circle\n");
			}
			
		}
		
	}

}