/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad 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 2 of the License, or * (at your option) any later version. * * Nomad 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 Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package net.sf.nmedit.jsynth.nomad.forms; import java.awt.BorderLayout; import java.awt.Component; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Vector; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiUnavailableException; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListCellRenderer; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import net.sf.nmedit.jsynth.midi.MidiUtils; public class NomadMidiDialogFrmHandler extends NomadMidiDialogFrm implements ItemListener { /** * */ private static final long serialVersionUID = 5654053362334343563L; public static final String INPUT_DEVICE_PROPERTY = "midi.input"; public static final String OUTPUT_DEVICE_PROPERTY = "midi.output"; private NomadMidiDialogFrm form; private boolean listHardwareDevices = true; private boolean listSoftwareDevices = false; private boolean listAvailableDevices = false; private MidiDevice.Info previousInput = null; private MidiDevice.Info previousOutput = null; private MidiDevice.Info internalSelectedInput = null; private MidiDevice.Info internalSelectedOutput = null; private boolean listPreviousIfUnavailable = true; private String currentText = "current"; public NomadMidiDialogFrmHandler() { this.form = this; initializeForm(); } private void setInternalSelectedInput(MidiDevice.Info info) { MidiDevice.Info oldValue = this.internalSelectedInput; MidiDevice.Info newValue = info; if (oldValue != newValue) { this.internalSelectedInput = info; firePropertyChange(INPUT_DEVICE_PROPERTY, oldValue, newValue); } } private void setInternalSelectedOutput(MidiDevice.Info info) { MidiDevice.Info oldValue = this.internalSelectedOutput; MidiDevice.Info newValue = info; if (oldValue != newValue) { this.internalSelectedOutput = info; firePropertyChange(OUTPUT_DEVICE_PROPERTY, oldValue, newValue); } } public void setCurrentText(String text) { this.currentText = text; } public String getCurrentText() { return currentText; } public boolean isSelectionDifferent() { return getPreviousOutput() != getSelectedOutput() || getPreviousInput() != getSelectedInput(); } public void setListPreviousIfUnavailable(boolean enable) { this.listPreviousIfUnavailable = enable; } public boolean isListPreviousIfUnavailableEnabled() { return listPreviousIfUnavailable; } public MidiDevice.Info getPreviousInput() { return previousInput; } public MidiDevice.Info getPreviousOutput() { return previousOutput; } public void setPreviousInput(MidiDevice.Info previousInput) { this.previousInput = previousInput; setInternalSelectedInput(previousInput); updateForm(); } public void setPreviousOutput(MidiDevice.Info previousOutput) { this.previousOutput = previousOutput; setInternalSelectedOutput(previousOutput); updateForm(); } public boolean isListingAvailableDeviceDevicesOnly() { return listAvailableDevices; } public boolean isHardwareDeviceListingEnabled() { return listHardwareDevices; } public boolean isSoftwareDeviceListingEnabled() { return listHardwareDevices; } public void setListingAvailableDeviceDevicesOnly(boolean availableOnly) { listAvailableDevices = availableOnly; } public void setHardwareDeviceListingEnabled(boolean enable) { listHardwareDevices = enable; } public void setSoftwareDeviceListingEnabled(boolean enable) { listSoftwareDevices = enable; } /** * Updates the list of input/output devices. */ public void refreshImmediatelly() { updateForm(); } public MidiDevice.Info getSelectedInput() { return getSelectedInfo(form.cbInDevices); } public MidiDevice.Info getSelectedOutput() { return getSelectedInfo(form.cbOutDevices); } public boolean setSelectedInput(MidiDevice.Info info) { return setSelectedInfo(form.cbInDevices, info); } public boolean setSelectedOutput(MidiDevice.Info info) { return setSelectedInfo(form.cbOutDevices, info); } private void initializeForm() { form.cbInDevices.addItemListener(this); form.cbOutDevices.addItemListener(this); form.cbInDevices.setRenderer(new MidiInfoRenderer(form.cbInDevices)); form.cbOutDevices.setRenderer(new MidiInfoRenderer(form.cbOutDevices)); updateForm(); } private void updateForm() { form.cbInDevices.setModel(new DefaultComboBoxModel(createDeviceList(true))); form.cbOutDevices.setModel(new DefaultComboBoxModel(createDeviceList(false))); if (form.cbInDevices.getItemCount()>0) { if (internalSelectedInput == null && previousInput != null) { setInternalSelectedInput(previousInput); } if (!(internalSelectedInput != null && setSelectedInput(internalSelectedInput))) form.cbInDevices.setSelectedIndex(0); } else { setInternalSelectedInput(null); } if (form.cbOutDevices.getItemCount()>0) { if (internalSelectedOutput == null && previousOutput != null) { setInternalSelectedOutput(previousOutput); } if (!(internalSelectedOutput != null && setSelectedOutput(internalSelectedOutput))) form.cbOutDevices.setSelectedIndex(0); } else { setInternalSelectedOutput(null); } form.cbInDevices.setEnabled(form.cbInDevices.getItemCount()>0); form.cbOutDevices.setEnabled(form.cbOutDevices.getItemCount()>0); updateLabels(true); updateLabels(false); form.cbInDevices.repaint(); form.cbOutDevices.repaint(); if (getPreviousInput() == null ^ form.cbInDevices.getItemCount()==0) firePropertyChange(INPUT_DEVICE_PROPERTY, getPreviousInput(), null); if (getPreviousOutput() == null ^ form.cbOutDevices.getItemCount()==0) firePropertyChange(OUTPUT_DEVICE_PROPERTY, getPreviousOutput(), null); } private MidiDevice.Info getSelectedInfo(JComboBox cb) { return (MidiDevice.Info) cb.getSelectedItem(); } private boolean setSelectedInfo(JComboBox cb, MidiDevice.Info info) { if (defaultModel(cb).getIndexOf(info)>=0) { // element exists cb.setSelectedItem(info); return true; } return false; } private DefaultComboBoxModel defaultModel(JComboBox cb) { return (DefaultComboBoxModel) cb.getModel(); } private Vector<MidiDevice.Info> createDeviceList(boolean inputs) { final boolean hw = listHardwareDevices; // collect hardware device ? final boolean sw = listSoftwareDevices; // but software devices ? final boolean onlyAvailable = listAvailableDevices; // list unavailable ones Vector<MidiDevice.Info> c = new Vector<MidiDevice.Info>(); MidiUtils.collectMidiDeviceInfo(c, hw, sw, inputs, !inputs, onlyAvailable); if (inputs) { checkAdd(c, previousInput, inputs); } else { checkAdd(c, previousOutput, !inputs); } return c; } private void checkAdd(Vector<MidiDevice.Info> c, MidiDevice.Info info, boolean input) { // info is null or already in the list if (info == null || c.contains(info)) return; // info is not in list if (listPreviousIfUnavailable) { try { if (input) { if (!MidiUtils.isInputDeviceAvailable(info)) { // not available c.add(info); } } else { if (!MidiUtils.isOutputDeviceAvailable(info)) { // not available c.add(info); } } } catch (MidiUnavailableException e) { // the device does not exist anymore - we do not add it } } } public void itemStateChanged(ItemEvent e) { if (e.getSource() == form.cbInDevices) { setInternalSelectedInput((MidiDevice.Info)form.cbInDevices.getSelectedItem()); updateLabels(true); } else if (e.getSource() == form.cbOutDevices) { setInternalSelectedOutput((MidiDevice.Info)form.cbOutDevices.getSelectedItem()); updateLabels(false); } } private void updateLabels(boolean forInputDevice) { if (forInputDevice) { updateLabels(form.cbInDevices, form.lblInVendor, form.lblInVersion, form.lblInDescription); } else { updateLabels(form.cbOutDevices, form.lblOutVendor, form.lblOutVersion, form.lblOutDescription); } } private void updateLabels(JComboBox cb, JLabel lblVendor, JLabel lblVersion, JLabel lblDescription) { final String NO_VALUE = "-"; MidiDevice.Info info = (MidiDevice.Info) cb.getSelectedItem(); if (info == null) { lblVendor.setText(NO_VALUE); lblVersion.setText(NO_VALUE); lblDescription.setText(NO_VALUE); } else { lblVendor.setText(info.getVendor()); lblVersion.setText(info.getVersion()); lblDescription.setText(info.getDescription()); } } public void refresh() { SwingUtilities.invokeLater(new Runnable() { public void run() { refreshImmediatelly(); } }); } /** * Renders the MidiDevice.Info list items of the JComboBox. * Instead of only showing the name of the device the renderer * appends the list index plus 1. Since this index is unique for each list * item it is possible to distinguish between devices which have * the equal names (usability). */ private class MidiInfoRenderer extends DefaultListCellRenderer { /** * */ private static final long serialVersionUID = 4870999830473752949L; private JComboBox cb; public MidiInfoRenderer(JComboBox cb) { this.cb = cb; } private DefaultComboBoxModel getModel() { return defaultModel(cb); } public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { String text; if (value == null) { text = "-none-"; } else { final MidiDevice.Info info = (MidiDevice.Info) value; // if the combobox is opened, the index is >= 0, but if the // combobox is closed, the displayed item has a negative index int id = (index<0 ? getModel().getIndexOf(info) : index)+1; text = info.getName()+" ("+(id)+")"; if (info.equals(getPreviousInput()) || info.equals(getPreviousOutput())) text += " ("+getCurrentText()+")"; if (cb.getSelectedIndex() == index) { text = "-> "+text; } } return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus); } } // For testing. public static void main(String[] args) { NomadMidiDialogFrmHandler dialog = new NomadMidiDialogFrmHandler(); dialog.setSoftwareDeviceListingEnabled(true); dialog.setPreviousInput(dialog.getSelectedInput()); dialog.setPreviousOutput(null); PropertyChangeListener l = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName() == INPUT_DEVICE_PROPERTY || evt.getPropertyName() == OUTPUT_DEVICE_PROPERTY) System.out.println(evt.getPropertyName()+": oldValue="+evt.getOldValue()+", newValue="+evt.getNewValue()); } }; dialog.refresh(); dialog.addPropertyChangeListener(l); JFrame f = new JFrame("Midi Dialog"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setBounds(10, 10, 400, 300); f.getContentPane().setLayout(new BorderLayout()); f.getContentPane().add(new JScrollPane(dialog)); f.setVisible(true); } }