/* 	J_InputSettings.java	Title:			Virtual Control Surface	Author:			root	Description:	Copyright © 2001 Jason LamportAll Rights ReservedImplements the movable-modal dialogue used to set the input settings on a particular virtual rack*/package com.strangelight.v4control;import com.strangelight.*;import com.strangelight.salsa.*;import com.strangelight.mactoolbox.*;import java.awt.*;import java.awt.event.*;import java.net.*;public class J_InputSettings 	extends 		J_Dialog 	implements 		V4ControlLib_interface, 		ActionListener,		ItemListener,		J_Cancellable {	protected Button cancel;	protected Button okay;	protected Checkbox auto_config;	protected J_DialControl	midi_channel;	protected J_DialControl	base_controller;	protected J_InputBus_Popup	input_bus;		protected static class DEFAULT {		static boolean auto_config = true;		static int midi_channel = 0;		static int base_controller = 0;		static short input_unique_id = -1;	}			private AutoConfig 	autoconfigd;		protected boolean cancelled = false;		public boolean was_cancelled() { return cancelled; }	public J_InputSettings(V_Rack owner, String title) {		super( owner, title, true );				setSize( new Dimension(450, 300) );		setLocation( new Point(5, 5) );		setResizable(false);		setBackground( J_DialControl.bg_color );				setLayout(gridbag);		//	setFont(new Font("Helvetica", Font.PLAIN, 14));		cancel = new Button( "Cancel" );		cancel.addActionListener( this );				okay = new Button( "Okay" );		okay.addActionListener( this );		midi_channel = new J_DialControl(DEFAULT.midi_channel);		base_controller = new J_DialControl(DEFAULT.base_controller);				input_bus = new J_InputBus_Popup(DEFAULT.input_unique_id);				auto_config = new Checkbox( 			"get settings from MIDI input  ", 			DEFAULT.auto_config 		);		auto_config.setBackground( Color.lightGray );		if ( DEFAULT.auto_config ) {			autoconfigd = new AutoConfig();		}		auto_config.addItemListener( this );				Label label1 = new Label("  Base-Controller  ");		label1.setBackground(Color.lightGray);		Label label2 = new Label("  MIDI-Channel  ");		label2.setBackground(Color.lightGray);				Panel button_panel = new Panel();		button_panel.add(cancel);		button_panel.add(new J_EmptyCell(12, 12) );		button_panel.add(okay);		add_space(0,0,20,16);				add_at(input_bus, 1, 1 );		add_space(2,1,20,16);		add_at(base_controller, 3, 1);		add_space(4,1,16,16);		add_at(midi_channel, 5, 1);				add_space(6, 4, 0, 0);		add_space(7,2,20, 4);				add_at( label1, 3, 3 );		add_at( label2, 5, 3 );		add_space(0,4,20,8);				add_at(auto_config, 1, 5, 5, 1, GridBagConstraints.WEST);				add_space(0,6,20,8);				add_at(button_panel, 3, 7, 4, 1, GridBagConstraints.EAST);		add_space(0,8,0,0);				setVisible( true );				okay.requestFocus();	}		public void close() {		autoconfigd.kill_and_ignore();/*	for some reason, AutoConfig takes a very long time to die, so we justsend it the kill signal and move on...		try {			autoconfigd.kill(10000);		} catch (InterruptedException x) {			throw new E_Unknown("autoconfigd wouldn't die!");		}*/				this.hide();	}		public void actionPerformed(ActionEvent e) {		if ( e.getSource() == cancel ) {			cancelled = true;			close();		} else if ( e.getSource() == okay ) {			cancelled = false;			close();		}	}		public void itemStateChanged(ItemEvent e) {		if ( e.getSource() == auto_config ) {			if ( e.getStateChange() == e.SELECTED ) {				if ( autoconfigd == null ) {					autoconfigd = new AutoConfig();				} else {					autoconfigd.resume();				}			} else if ( e.getStateChange() == e.DESELECTED ) {				autoconfigd.suspend();			}		}	}	public void set_channel(int channel) {		midi_channel.setValue(channel);	}	public void set_controller(int controller_base) {		base_controller.setValue(controller_base);	}		public void set_source_node(short source_unique_id) {		input_bus.set_source_node(source_unique_id);	}	protected final class AutoConfig 		extends J_PointerStruct 		implements Runnable 	{	/*		Note that we are implementing suspend() and resume() methods 		for this *Runnable* object using Sun-approved techniques.		(The corresponding methods in the Thread class are		*deprecated*, but because we're implementing Runnable, rather		than extending Thread, we can implement these methods'		functionality in a safe, Sun-approved way.)	 */		private static final int SLEEP_MS = 100;		private static final int CHANNEL_BYTE_OFFSET = 14;		private static final int CONTROLLER_BYTE_OFFSET = 15;		private static final int SOURCE_UNIQUE_ID_OFFSET = 12;		private static final int CONTROLLER_EVENT = 0xB0;				private J_InputSettings	owner;		private Thread		the_thread;		private volatile boolean suspended;		private volatile boolean keep_running;			public AutoConfig() {			super( j_request_controller_msgs() );			the_thread = new Thread(this);			suspended = false;			keep_running = true;			the_thread.start();		}				public synchronized void suspend() {			suspended = true;		}				public synchronized void resume() {			if ( suspended ) {				suspended = false;				notifyAll();			}		}				public synchronized void kill_and_ignore() {			resume();			keep_running = false;		}				public synchronized void kill() throws InterruptedException {			resume();			keep_running = false;			the_thread.join();		}				public synchronized void kill(long msecs) throws InterruptedException {			resume();			keep_running = false;			the_thread.join(msecs);		}				public void run() {			int channel, event_type, controller_base, foo = 0;			short source_unique_id;			byte channel_byte;									while ( keep_running ) {				try {					channel_byte = getByteAt(CHANNEL_BYTE_OFFSET);					event_type = channel_byte & 0xF0;					if ( event_type == CONTROLLER_EVENT ) {						channel = channel_byte & 0x0F;						controller_base = getByteAt(CONTROLLER_BYTE_OFFSET) >> 3;						source_unique_id = getShortAt(SOURCE_UNIQUE_ID_OFFSET);						set_channel(channel);						set_controller(controller_base);						set_source_node(source_unique_id);						setByteAt( CHANNEL_BYTE_OFFSET, (byte) 0 );						//	"consume" this MIDI packet					}					Thread.sleep(SLEEP_MS);					synchronized (this) {						while (suspended) {							wait();							setByteAt( CHANNEL_BYTE_OFFSET, (byte) 0 );							//	clear whatever MIDI came in while we were suspended						}					}				} catch ( InterruptedException ex ) {								}			}		}				protected void finalize() {			j_unrequest_controller_msgs();		}			}	private static native int j_request_controller_msgs();	//	private static native int j_get_last_controller_msg();	private static native void j_unrequest_controller_msgs();}