/*
* Jajuk
* Copyright (C) The Jajuk Team
* http://jajuk.info
*
* 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 2
* of the License, or 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package org.jajuk.ui.wizard;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;
import org.jajuk.base.Device;
import org.jajuk.base.DeviceManager;
import org.jajuk.base.DirectoryManager;
import org.jajuk.events.JajukEvent;
import org.jajuk.events.JajukEvents;
import org.jajuk.events.ObservationManager;
import org.jajuk.ui.widgets.InformationJPanel;
import org.jajuk.ui.widgets.JajukFileChooser;
import org.jajuk.ui.widgets.JajukJDialog;
import org.jajuk.ui.widgets.OKCancelPanel;
import org.jajuk.ui.windows.JajukMainWindow;
import org.jajuk.util.Const;
import org.jajuk.util.IconLoader;
import org.jajuk.util.JajukFileFilter;
import org.jajuk.util.JajukIcons;
import org.jajuk.util.Messages;
import org.jajuk.util.UtilString;
import org.jajuk.util.error.JajukException;
import org.jajuk.util.filters.DirectoryFilter;
import org.jajuk.util.log.Log;
/**
* Device creation wizard.
*/
public class DeviceWizard extends JajukJDialog implements ActionListener, Const {
/** The Constant WRAP. */
private static final String WRAP = "wrap";
/** Generated serialVersionUID. */
private static final long serialVersionUID = 1L;
/** Device type combo. */
@SuppressWarnings("rawtypes")
private final JComboBox jcbType;
/** Device name text field. */
private final JTextField jtfName;
/** Device url text field. */
private final JTextField jtfUrl;
/** Device url path selector button. */
private final JButton jbUrl;
/** Auto-refresh device checkbox. */
private final JCheckBox jcbRefresh;
/** Auto-mount checkbox. */
private final JCheckBox jcbAutoMount;
/** Auto-refresh interval. */
private final JTextField jtfAutoRefresh;
/** Device sync checkbox. */
private final JCheckBox jcboxSynchronized;
/** Other device combo. */
@SuppressWarnings("rawtypes")
private final JComboBox jcbSynchronized;
/** Bidi sync choice. */
private final JRadioButton jrbBidirSynchro;
/** Unidir sync choice. */
private final JRadioButton jrbUnidirSynchro;
/** Ok Cancel panel. */
private final OKCancelPanel okp;
/** New device flag. */
private boolean bNew = true;
/** Current device. */
private Device device;
/** All devices expect itself. */
private final List<Device> devices;
/** Initial URL*. */
private String sInitialURL;
/** A convenient NumberFormat instance. */
private NumberFormat nformat = NumberFormat.getInstance();
/**
* Device wizard by default, is used for void configuration.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public DeviceWizard() {
super(JajukMainWindow.getInstance(), true);
devices = DeviceManager.getInstance().getDevices();
addWindowListener(new WindowAdapter() {
@Override
public void windowActivated(final WindowEvent e) {
jtfName.requestFocusInWindow();
}
});
setTitle(Messages.getString("DeviceWizard.0"));
setModal(true);
setLocation(JajukMainWindow.getInstance().getX() + 100,
JajukMainWindow.getInstance().getY() + 100);
JLabel jlType = new JLabel(Messages.getString("DeviceWizard.1"));
jcbType = new JComboBox();
for (Device.Type type : Device.Type.values()) {
jcbType.addItem(Device.getTypeLabel(type));
}
JLabel jlName = new JLabel(Messages.getString("DeviceWizard.2"));
jtfName = new JTextField();
jtfName.setToolTipText(Messages.getString("DeviceWizard.45"));
JLabel jlUrl = new JLabel(Messages.getString("DeviceWizard.3"));
jtfUrl = new JTextField();
jtfUrl.setToolTipText(Messages.getString("DeviceWizard.46"));
jbUrl = new JButton(IconLoader.getIcon(JajukIcons.OPEN_FILE));
jbUrl.setToolTipText(Messages.getString("DeviceWizard.43"));
jbUrl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
jbUrl.addActionListener(this);
// we disable focus for url and mount url buttons to facilitate
// navigation
jbUrl.setFocusable(false);
jcbRefresh = new JCheckBox(Messages.getString("DeviceWizard.7"));
jcbRefresh.setToolTipText(Messages.getString("DeviceWizard.48"));
jcbRefresh.addActionListener(this);
jcbAutoMount = new JCheckBox(Messages.getString("DeviceWizard.8"));
jcbAutoMount.setToolTipText(Messages.getString("DeviceWizard.49"));
jcbAutoMount.addActionListener(this);
JLabel jlAutoRefresh = new JLabel(Messages.getString("DeviceWizard.53"));
jlAutoRefresh.setToolTipText(Messages.getString("DeviceWizard.50"));
JLabel jlMinutes = new JLabel(Messages.getString("DeviceWizard.54"));
/* jtfAutoRefresh rules : Minimum delay is half a minute */
jtfAutoRefresh = new JTextField();
jtfAutoRefresh.setName(Messages.getString("DeviceWizard.54"));
jtfAutoRefresh.setToolTipText(Messages.getString("DeviceWizard.50"));
jcboxSynchronized = new JCheckBox(Messages.getString("DeviceWizard.10"));
jcboxSynchronized.setToolTipText(Messages.getString("DeviceWizard.51"));
jcboxSynchronized.addActionListener(this);
jcbSynchronized = new JComboBox();
// populate combo
for (Device device1 : devices) {
jcbSynchronized.addItem(device1.getName());
}
jcbSynchronized.setEnabled(false);
jcbSynchronized.setToolTipText(Messages.getString("DeviceWizard.52"));
// Default automount behavior
jcbType.addActionListener(this);
ButtonGroup bgSynchro = new ButtonGroup();
jrbUnidirSynchro = new JRadioButton(Messages.getString("DeviceWizard.11"));
jrbUnidirSynchro.setToolTipText(Messages.getString("DeviceWizard.12"));
jrbUnidirSynchro.setBorder(BorderFactory.createEmptyBorder(0, 25, 0, 0));
jrbUnidirSynchro.setEnabled(false);
jrbUnidirSynchro.addActionListener(this);
jrbBidirSynchro = new JRadioButton(Messages.getString("DeviceWizard.13"));
jrbBidirSynchro.setToolTipText(Messages.getString("DeviceWizard.14"));
jrbBidirSynchro.setBorder(BorderFactory.createEmptyBorder(0, 25, 0, 0));
jrbBidirSynchro.setEnabled(false);
jrbBidirSynchro.addActionListener(this);
bgSynchro.add(jrbBidirSynchro);
bgSynchro.add(jrbUnidirSynchro);
// buttons
okp = new OKCancelPanel(this);
// Add items
setLayout(new MigLayout("insets 10,gapx 10, gapy 15", "[][grow]"));
add(jlType);
add(jcbType, "grow,wrap");
add(jlName);
add(jtfName, "grow,wrap");
add(jlUrl);
add(jtfUrl, "split 2,growx");
add(jbUrl, WRAP);
add(jlAutoRefresh);
add(jtfAutoRefresh, "grow,split 2");
add(jlMinutes, WRAP);
add(jcbRefresh, WRAP);
add(jcbAutoMount, WRAP);
add(jcboxSynchronized);
add(jcbSynchronized, "grow,wrap");
add(jrbUnidirSynchro, "left,gap left 20,span,wrap");
add(jrbBidirSynchro, "left,gap left 20,span,wrap");
add(okp, "span,right");
// Set default behaviors
if (jcbSynchronized.getItemCount() == 0) {
jcboxSynchronized.setEnabled(false);
jcbSynchronized.setEnabled(false);
jrbBidirSynchro.setEnabled(false);
}
getRootPane().setDefaultButton(okp.getOKButton());
pack();
okp.getOKButton().requestFocusInWindow();
}
private void validateAutoRefreshDelay() throws JajukException {
// Auto-refresh interval validation : should be 0 or a double >= 0.5
String autoRefreshDelay = jtfAutoRefresh.getText();
double value;
try {
value = nformat.parse(autoRefreshDelay).doubleValue();
} catch (Exception ex) {
throw new JajukException(137);
}
// If value is zero, validate the user input
if (value != 0 && value < 0.5d) {
throw new JajukException(184);
}
}
private void validateDeviceName() throws JajukException {
// Validate device name : not void and not an existing name
if (UtilString.isEmpty(jtfName.getText())) {
throw new JajukException(183);
}
for (Device deviceToCheck : DeviceManager.getInstance().getDevices()) {
// check for a new device with an existing name
if (bNew && jtfName.getText().equalsIgnoreCase(deviceToCheck.getName())) {
throw new JajukException(19);
}
}
}
private void validateDeviceLocation() throws JajukException {
// Validate device url : not void and maps an existing device
if (UtilString.isEmpty(jtfUrl.getText())) {
throw new JajukException(183);
}
String url = jtfUrl.getText();
if (!new File(url).exists()) {
throw new JajukException(143);
}
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(final ActionEvent e) {
if (e.getSource() == jcboxSynchronized) {
handleSynchronized();
} else if (e.getSource() == okp.getOKButton()) {
handleOk();
} else if (e.getSource() == okp.getCancelButton()) {
dispose(); // close window
} else if (e.getSource() == jbUrl) {
handleUrl();
} else if (e.getSource() == jcbType) {
handleType();
}
}
/**
* Handle type.
*/
private void handleType() {
switch (jcbType.getSelectedIndex()) {
case 0: // directory
jcbAutoMount.setSelected(true);
if (bNew) {
jtfAutoRefresh.setText(nformat.format(Const.DEFAULT_REFRESH_INTERVAL_DIRECTORY));
}
break;
case 1: // file cd
jcbAutoMount.setSelected(false);
if (bNew) {
jtfAutoRefresh.setText(nformat.format(Const.DEFAULT_REFRESH_INTERVAL_CD));
}
break;
case 2: // network drive
jcbAutoMount.setSelected(true);
// no auto-refresh by default for network drive
if (bNew) {
jtfAutoRefresh.setText(nformat.format(Const.DEFAULT_REFRESH_INTERVAL_NETWORK_DRIVE));
}
break;
case 3: // ext dd
jcbAutoMount.setSelected(true);
if (bNew) {
jtfAutoRefresh.setText(nformat.format(Const.DEFAULT_REFRESH_INTERVAL_EXTERNAL_DRIVE));
}
break;
case 4: // player
jcbAutoMount.setSelected(false);
if (bNew) {
jtfAutoRefresh.setText(nformat.format(Const.DEFAULT_REFRESH_INTERVAL_PLAYER));
}
break;
}
}
/**
* Handle url.
*
* @throws HeadlessException the headless exception
*/
private void handleUrl() throws HeadlessException {
final JajukFileChooser jfc = new JajukFileChooser(new JajukFileFilter(
DirectoryFilter.getInstance()));
jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
jfc.setDialogTitle(Messages.getString("DeviceWizard.43"));
jfc.setMultiSelectionEnabled(false);
final String sUrl = jtfUrl.getText();
if (!"".equals(sUrl)) {
// if url is already set, use it as root directory
jfc.setCurrentDirectory(new File(sUrl));
}
final int returnVal = jfc.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
final java.io.File file = jfc.getSelectedFile();
jtfUrl.setText(file.getAbsolutePath());
}
}
/**
* Handle ok.
*/
private void handleOk() {
// validate fields
try {
validateAutoRefreshDelay();
validateDeviceName();
validateDeviceLocation();
} catch (JajukException je) {
Messages.showErrorMessage(je.getCode());
return;
}
new Thread("Device Wizard Action Thread") {
@Override
public void run() {
if (bNew) {
int indexType = jcbType.getSelectedIndex();
Device.Type type = Device.Type.values()[indexType];
device = DeviceManager.getInstance().registerDevice(jtfName.getText(), type,
jtfUrl.getText());
}
device.setProperty(Const.XML_DEVICE_AUTO_MOUNT, jcbAutoMount.isSelected());
try {
device.setProperty(Const.XML_DEVICE_AUTO_REFRESH, nformat.parse(jtfAutoRefresh.getText())
.doubleValue());
} catch (ParseException e) {
// Should not happen thanks GUI validators
Log.error(e);
}
device.setProperty(Const.XML_TYPE, Long.valueOf(jcbType.getSelectedIndex()));
device.setUrl(jtfUrl.getText());
if (jcbSynchronized.isEnabled() && (jcbSynchronized.getSelectedItem() != null)) {
Device selected = DeviceManager.getInstance().getDeviceByName(
(String) jcbSynchronized.getSelectedItem());
device.setProperty(Const.XML_DEVICE_SYNCHRO_SOURCE, selected.getID());
if (jrbBidirSynchro.isSelected()) {
device.setProperty(Const.XML_DEVICE_SYNCHRO_MODE, Const.DEVICE_SYNCHRO_MODE_BI);
} else {
device.setProperty(Const.XML_DEVICE_SYNCHRO_MODE, Const.DEVICE_SYNCHRO_MODE_UNI);
}
} else { // no synchro
device.removeProperty(Const.XML_DEVICE_SYNCHRO_MODE);
device.removeProperty(Const.XML_DEVICE_SYNCHRO_SOURCE);
}
// Force deep refresh if it is a new device or if URL changed
if (jcbRefresh.isSelected() && bNew) {
try {
// Drop existing directory to avoid phantom directories if
// existing device
DirectoryManager.getInstance().removeDirectory(device.getID());
device.refresh(true, false, false, null);
} catch (final Exception e2) {
Log.error(112, device.getName(), e2);
Messages.showErrorMessage(112, device.getName());
}
} else if (sInitialURL != null && !sInitialURL.equals(jtfUrl.getText())) {
// If user changed the URL, force refresh
try {
// try to remount the device
if (!device.isMounted()) {
boolean mounted = device.mount(true);
// Leave if user canceled device mounting
if (!mounted) {
dispose();
return;
}
}
// Keep previous references when changing device url
device.refresh(true, false, true, null);
} catch (final Exception e2) {
Log.error(112, device.getName(), e2);
Messages.showErrorMessage(112, device.getName());
}
}
ObservationManager.notify(new JajukEvent(JajukEvents.DEVICE_REFRESH));
dispose();
if (bNew) {
InformationJPanel.getInstance().setMessage(Messages.getString("DeviceWizard.44"),
InformationJPanel.MessageType.INFORMATIVE);
}
}
}.start();
}
/**
* Handle synchronized.
*/
private void handleSynchronized() {
if (jcboxSynchronized.isSelected()) {
jcbSynchronized.setEnabled(true);
jrbBidirSynchro.setEnabled(true);
jrbUnidirSynchro.setEnabled(true);
} else {
jcbSynchronized.setEnabled(false);
jrbBidirSynchro.setEnabled(false);
jrbUnidirSynchro.setEnabled(false);
}
}
/**
* Update widgets for device property state.
*
* @param device1
*/
@SuppressWarnings("unchecked")
public void updateWidgets(final Device device1) {
bNew = false;
setTitle(Messages.getString("DeviceWizard.0") + " : " + device1.getName());
this.device = device1;
jcbSynchronized.removeAllItems();
devices.clear();
// set default values for widgets
updateWidgetsDefault();
List<Device> devices2 = DeviceManager.getInstance().getDevices();
for (Device device2 : devices2) {
if (!device2.equals(device1)) {
devices.add(device2);
jcbSynchronized.addItem(device2.getName());
}
}
// then, specifics
jcbType.setSelectedItem(device1.getDeviceTypeS());
jtfName.setText(device1.getName());
jtfName.setEnabled(false); // device name cannot be changed
jtfUrl.setText(device1.getUrl());
sInitialURL = device1.getUrl();
jcbRefresh.setEnabled(false); // no instant refresh for updates
jcbRefresh.setSelected(false);
jcbAutoMount.setSelected(true);
if (device1.getBooleanValue(Const.XML_DEVICE_AUTO_MOUNT)) {
jcbAutoMount.setSelected(true);
} else {
jcbAutoMount.setSelected(false);
}
jtfAutoRefresh.setText(nformat.format(device1.getDoubleValue(Const.XML_DEVICE_AUTO_REFRESH)));
if (jcbSynchronized.getItemCount() == 0) {
jcboxSynchronized.setEnabled(false);
jcbSynchronized.setEnabled(false);
jrbBidirSynchro.setEnabled(false);
}
if (device1.containsProperty(Const.XML_DEVICE_SYNCHRO_SOURCE)) {
final String sSynchroSource = device1.getStringValue(Const.XML_DEVICE_SYNCHRO_SOURCE);
jrbBidirSynchro.setEnabled(true);
jrbUnidirSynchro.setEnabled(true);
jcboxSynchronized.setSelected(true);
jcboxSynchronized.setEnabled(true);
jcbSynchronized.setEnabled(true);
Device toBeSelected = DeviceManager.getInstance().getDeviceByID(sSynchroSource);
jcbSynchronized.setSelectedItem(toBeSelected.getName());
if (Const.DEVICE_SYNCHRO_MODE_BI.equals(device1.getValue(Const.XML_DEVICE_SYNCHRO_MODE))) {
jrbBidirSynchro.setSelected(true);
} else {
jrbUnidirSynchro.setSelected(true);
}
}
}
/**
* Update widgets for default state.
*/
public void updateWidgetsDefault() {
jcbRefresh.setSelected(true);
jcbAutoMount.setSelected(true);
jtfAutoRefresh.setText(nformat.format(Const.DEFAULT_REFRESH_INTERVAL_DIRECTORY));
jcboxSynchronized.setSelected(false);
jrbUnidirSynchro.setSelected(true);// default synchro mode
jrbBidirSynchro.setEnabled(false);
}
}