/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* 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
******************************************************************************/
package settings;
import java.awt.Color;
import java.awt.Image;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import javax.swing.ImageIcon;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import map.DefaultIcon;
import map.MapIcon;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import settings.ColorSetting.ColorSettingName;
import source.recording.RecordingConfiguration;
import source.tuner.TunerConfiguration;
import source.tuner.TunerConfigurationAssignment;
import source.tuner.TunerType;
import source.tuner.airspy.AirspyTunerConfiguration;
import source.tuner.fcd.proV1.FCD1TunerConfiguration;
import source.tuner.fcd.proplusV2.FCD2TunerConfiguration;
import source.tuner.hackrf.HackRFTunerConfiguration;
import source.tuner.rtl.e4k.E4KTunerConfiguration;
import source.tuner.rtl.r820t.R820TTunerConfiguration;
public class SettingsManager
{
private final static Logger mLog =
LoggerFactory.getLogger( SettingsManager.class );
private Settings mSettings = new Settings();
private ArrayList mListeners =
new ArrayList();
public static final String DEFAULT_ICON = "No Icon";
public String mCurrentDefaultIconName = DEFAULT_ICON;
public static final int DEFAULT_ICON_SIZE = 12;
private HashMap mIcons = new HashMap();
private HashMap mResizedIcons =
new HashMap();
public SettingsManager()
{
init();
}
/**
* Loads settings from the current settings file, or the default settings file,
* as specified in the current SDRTrunk system settings
*/
private void init()
{
SystemProperties props = SystemProperties.getInstance();
Path settingsFolder = props.getApplicationFolder( "settings" );
String defaultSettingsFile =
props.get( "settings.defaultFilename", "settings.xml" );
String settingsFile =
props.get( "settings.currentFilename", defaultSettingsFile );
load( settingsFolder.resolve( settingsFile ) );
refreshIcons();
}
/**
* Reloads the icon hash map, and sets up the default iconology. If the
* saved default icon name is not part of the current set of icons, we
* set the default icon to the standard system default name, which should
* be guaranteed to be available.
*/
public void refreshIcons()
{
mIcons.clear();
mCurrentDefaultIconName = DEFAULT_ICON;
DefaultIcon defaultIcon = getDefaultIcon();
ArrayList icons = mSettings.getMapIcons();
icons.addAll( getStandardIcons() );
for( MapIcon icon: icons )
{
mIcons.put( icon.getName(), icon );
if( icon.getName().contentEquals( defaultIcon.getName() ) )
{
mCurrentDefaultIconName = defaultIcon.getName();
icon.setDefaultIcon( true );
}
else
{
icon.setDefaultIcon( false );
}
}
}
public Settings getSettings()
{
return mSettings;
}
public void setSettings( Settings settings )
{
mSettings = settings;
}
public Setting getSetting( String name )
{
return mSettings.getSetting( name );
}
/**
* Returns the current setting, or if the setting doesn't exist
* returns a newly created setting with the specified parameters
*/
public ColorSetting getColorSetting( ColorSettingName name )
{
ColorSetting setting = mSettings.getColorSetting( name );
if( setting == null )
{
setting = new ColorSetting( name );
addSetting( setting );
}
return setting;
}
/**
* Fetches the current setting and applies the parameter(s) to it. Creates
* the setting if it does not exist
*/
public void setColorSetting( ColorSettingName name, Color color )
{
ColorSetting setting = getColorSetting( name );
setting.setColor( color );
broadcastSettingChange( setting );
save();
}
public void resetColorSetting( ColorSettingName name )
{
setColorSetting( name, name.getDefaultColor() );
}
public void resetAllColorSettings()
{
for( ColorSetting color: mSettings.getColorSettings() )
{
resetColorSetting( color.getColorSettingName() );
}
}
/**
* Returns the current setting, or if the setting doesn't exist
* returns a newly created setting with the specified parameters
*/
public FileSetting getFileSetting( String name, String defaultPath )
{
FileSetting setting = mSettings.getFileSetting( name );
if( setting == null )
{
setting = new FileSetting( name, defaultPath );
addSetting( setting );
}
return setting;
}
/**
* Fetches the current setting and applies the parameter(s) to it. Creates
* the setting if it does not exist
*/
public void setFileSetting( String name, String path )
{
FileSetting setting = getFileSetting( name, path );
setting.setPath( path );
broadcastSettingChange( setting );
save();
}
public MapIcon[] getMapIcons()
{
MapIcon[] returnIcons = new MapIcon[mIcons.values().size() ];
return mIcons.values().toArray( returnIcons );
}
private ArrayList getStandardIcons()
{
ArrayList icons = new ArrayList();
MapIcon defaultIcon = new MapIcon( DEFAULT_ICON, "images/no_icon.png", false );
icons.add( defaultIcon );
MapIcon ambulance =
new MapIcon( "Ambulance", "images/ambulance.png", false );
icons.add( ambulance );
MapIcon blockTruck =
new MapIcon( "Block Truck", "images/concrete_block_truck.png", false );
icons.add( blockTruck );
MapIcon cwid = new MapIcon( "CWID", "images/cwid.png", false );
icons.add( cwid );
MapIcon dispatcher =
new MapIcon( "Dispatcher", "images/dispatcher.png", false );
icons.add( dispatcher );
MapIcon dumpTruck =
new MapIcon( "Dump Truck", "images/dump_truck_red.png", false );
icons.add( dumpTruck );
MapIcon fireTruck =
new MapIcon( "Fire Truck", "images/fire_truck.png", false );
icons.add( fireTruck );
MapIcon garbageTruck =
new MapIcon( "Garbage Truck", "images/garbage_truck.png", false );
icons.add( garbageTruck );
MapIcon loader = new MapIcon( "Loader", "images/loader.png", false );
icons.add( loader );
MapIcon police =
new MapIcon( "Police", "images/police.png", false );
icons.add( police );
MapIcon propaneTruck =
new MapIcon( "Propane Truck", "images/propane_truck.png", false );
icons.add( propaneTruck );
MapIcon rescueTruck =
new MapIcon( "Rescue Truck", "images/rescue_truck.png", false );
icons.add( rescueTruck );
MapIcon schoolBus =
new MapIcon( "School Bus", "images/school_bus.png", false );
icons.add( schoolBus );
MapIcon taxi =
new MapIcon( "Taxi", "images/taxi.png", false );
icons.add( taxi );
MapIcon train =
new MapIcon( "Train", "images/train.png", false );
icons.add( train );
MapIcon transportBus =
new MapIcon( "Transport Bus", "images/opt_bus.png", false );
icons.add( transportBus );
MapIcon van =
new MapIcon( "Van", "images/van.png", false );
icons.add( van );
return icons;
}
/**
* Deletes the map icon
*/
public void deleteMapIcon( MapIcon icon )
{
MapIcon existing = mIcons.get( icon.getName() );
if( existing != null )
{
mIcons.remove( icon.getName() );
mSettings.removeSetting( existing );
broadcastSettingDeleted( existing );
save();
}
}
/**
* Adds the icon, overwriting any existing icon with the same name
* @param icon
*/
public void addMapIcon( MapIcon icon )
{
MapIcon existing = mIcons.get( icon.getName() );
if( existing == null )
{
MapIcon newIcon = new MapIcon( icon.getName(), icon.getPath() );
mIcons.put( newIcon.getName(), newIcon );
addSetting( newIcon );
}
else
{
existing.setName( icon.getName() );
existing.setPath( icon.getPath() );
broadcastSettingChange( existing );
save();
}
}
public void updatMapIcon( MapIcon icon, String newName, String newPath )
{
MapIcon existing = mSettings.getMapIcon( icon.getName() );
if( existing == null )
{
addMapIcon( new MapIcon( newName, newPath ) );
}
else
{
existing.setName( newName );
existing.setPath( newPath );
broadcastSettingChange( existing );
save();
}
}
/**
* Returns named map icon scaled to height, if necessary.
* @param name - name of icon
* @param heigh - height of icon in pixels
*
* @return - ImageIcon or null
*/
public ImageIcon getImageIcon( String name, int height )
{
if( name != null )
{
String mergedName = name + height;
if( mResizedIcons.containsKey( mergedName ) )
{
return mResizedIcons.get( mergedName );
}
MapIcon mapIcon = mIcons.get( name );
if( mapIcon != null )
{
if( mapIcon.getImageIcon().getIconHeight() > height )
{
ImageIcon scaled = getScaledIcon( mapIcon.getImageIcon(), height );
mResizedIcons.put( mergedName, scaled );
return scaled;
}
else
{
mResizedIcons.put( mergedName, mapIcon.getImageIcon() );
}
}
/* Use the default Icon */
String mergedDefault = mCurrentDefaultIconName + height;
if( mResizedIcons.containsKey( mergedDefault ) )
{
return mResizedIcons.get( mergedDefault );
}
else
{
MapIcon defaultIcon = mIcons.get( mCurrentDefaultIconName );
if( defaultIcon != null )
{
if( defaultIcon.getImageIcon().getIconHeight() > height )
{
ImageIcon scaledDefault = getScaledIcon( defaultIcon.getImageIcon(), height );
mResizedIcons.put( mergedDefault, scaledDefault );
return scaledDefault;
}
else
{
mResizedIcons.put( mergedDefault, defaultIcon.getImageIcon() );
return defaultIcon.getImageIcon();
}
}
}
/* Something happened ... the above should always return an icon */
mLog.error( "couldn't return an icon named [" + name + "] of heigh [" + height + "]" );
}
return null;
}
public static ImageIcon getScaledIcon( ImageIcon original, int height )
{
double scale = (double)original.getIconHeight() / (double)height;
int scaledWidth = (int)( (double)original.getIconWidth() / scale );
Image scaledImage = original.getImage().getScaledInstance( scaledWidth,
height, java.awt.Image.SCALE_SMOOTH );
return new ImageIcon( scaledImage );
}
public MapIcon getMapIcon( String name )
{
if( mIcons.containsKey( name ) )
{
return mIcons.get( name );
}
/* Return the default */
if( mIcons.containsKey( mCurrentDefaultIconName ) )
{
return mIcons.get( mCurrentDefaultIconName );
}
/* Return the real default */
return mIcons.get( DEFAULT_ICON );
}
/**
* Returns the named map icon, or creates it with the default path
* @param name - name of the icon
* @param defaultPath - path to icon image file
* @return - map icon
*/
public MapIcon getMapIcon( String name, String defaultPath )
{
MapIcon icon = mSettings.getMapIcon( name );
if( icon == null )
{
icon = new MapIcon( name, defaultPath );
addSetting( icon );
}
return icon;
}
public DefaultIcon getDefaultIcon()
{
DefaultIcon icon = mSettings.getDefaultIcon();
if( icon == null )
{
icon = new DefaultIcon( DEFAULT_ICON );
addSetting( icon );
}
return icon;
}
public void setDefaultIcon( MapIcon icon )
{
if( mIcons.containsValue( icon ) )
{
MapIcon currentDefault = mIcons.get( mCurrentDefaultIconName );
currentDefault.setDefaultIcon( false );
icon.setDefaultIcon( true );
mCurrentDefaultIconName = icon.getName();
DefaultIcon defaultIconSetting = getDefaultIcon();
defaultIconSetting.setName( icon.getName() );
save();
broadcastSettingChange( defaultIconSetting );
}
}
/**
* Adds the setting and stores the set of settings
* @param setting
*/
private void addSetting( Setting setting )
{
mSettings.addSetting( setting );
save();
broadcastSettingChange( setting );
}
public ArrayList getRecordingConfigurations()
{
return mSettings.getRecordingConfigurations();
}
public void addRecordingConfiguration( RecordingConfiguration config )
{
mSettings.addRecordingConfiguration( config );
save();
}
public void removeRecordingConfiguration( RecordingConfiguration config )
{
mSettings.removeRecordingConfiguration( config );
save();
}
public ArrayList getTunerConfigurations( TunerType type )
{
ArrayList configs = getSettings()
.getTunerConfigurations( type );
if( configs.isEmpty() )
{
configs.add( addNewTunerConfiguration( type, "Default" ) );
}
return configs;
}
public void deleteTunerConfiguration( TunerConfiguration config )
{
getSettings().removeTunerConfiguration( config );
save();
}
public TunerConfiguration addNewTunerConfiguration( TunerType type,
String name )
{
switch( type )
{
case AIRSPY_R820T:
AirspyTunerConfiguration airspyConfig =
new AirspyTunerConfiguration( name );
getSettings().addTunerConfiguration( airspyConfig );
save();
return airspyConfig;
case ELONICS_E4000:
E4KTunerConfiguration e4KConfig =
new E4KTunerConfiguration( name );
getSettings().addTunerConfiguration( e4KConfig );
save();
return e4KConfig;
case FUNCUBE_DONGLE_PRO:
FCD1TunerConfiguration config =
new FCD1TunerConfiguration( name );
getSettings().addTunerConfiguration( config );
save();
return config;
case FUNCUBE_DONGLE_PRO_PLUS:
FCD2TunerConfiguration configPlus =
new FCD2TunerConfiguration( name );
getSettings().addTunerConfiguration( configPlus );
save();
return configPlus;
case HACKRF:
HackRFTunerConfiguration hackConfig =
new HackRFTunerConfiguration( name );
getSettings().addTunerConfiguration( hackConfig );
save();
return hackConfig;
case RAFAELMICRO_R820T:
R820TTunerConfiguration r820TConfig =
new R820TTunerConfiguration( name );
getSettings().addTunerConfiguration( r820TConfig );
save();
return r820TConfig;
default:
throw new IllegalArgumentException( "tuner type is unrecognized [" +
type.toString() + "]" );
}
}
public TunerConfigurationAssignment getSelectedTunerConfiguration(
TunerType type, String address )
{
return mSettings.getConfigurationAssignment( type, address );
}
public void setSelectedTunerConfiguration( TunerType type,
String address, TunerConfiguration config )
{
mSettings.setConfigurationAssignment( type, address, config.getName() );
save();
}
public MapViewSetting getMapViewSetting( String name, GeoPosition position, int zoom )
{
MapViewSetting loc = mSettings.getMapViewSetting( name );
if( loc != null )
{
return loc;
}
else
{
MapViewSetting newLoc = new MapViewSetting( name, position, zoom );
addSetting( newLoc );
return newLoc;
}
}
public void setMapViewSetting( String name, GeoPosition position, int zoom )
{
MapViewSetting loc = getMapViewSetting( name, position, zoom );
loc.setGeoPosition( position );
loc.setZoom( zoom );
save();
}
public void save()
{
JAXBContext context = null;
SystemProperties props = SystemProperties.getInstance();
Path settingsPath = props.getApplicationFolder( "settings" );
String settingsDefault = props.get( "settings.defaultFilename",
"settings.xml" );
String settingsCurrent = props.get( "settings.currentFilename",
settingsDefault );
Path filePath = settingsPath.resolve( settingsCurrent );
File outputFile = new File( filePath.toString() );
try
{
if( !outputFile.exists() )
{
outputFile.createNewFile();
}
}
catch( Exception e )
{
mLog.error( "couldn't create file to save settings [" + filePath.toString() + "]", e );
}
OutputStream out = null;
try
{
out = new FileOutputStream( outputFile );
try
{
context = JAXBContext.newInstance( Settings.class );
Marshaller m = context.createMarshaller();
m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
m.marshal( mSettings, out );
}
catch ( JAXBException e )
{
mLog.error( "jaxb exception while saving settings", e );
}
}
catch ( Exception e )
{
mLog.error( "couldn't open outputstream to save settings [" + filePath.toString() + "]" );
}
finally
{
if( out != null )
{
try
{
out.close();
}
catch ( IOException e )
{
e.printStackTrace();
}
}
}
}
/**
* Erases current settings and loads settings from the settingsPath filename,
* if it exists.
*/
public void load( Path settingsPath )
{
if( Files.exists( settingsPath ) )
{
mLog.info( "loading settings file [" + settingsPath.toString() + "]" );
JAXBContext context = null;
InputStream in = null;
try
{
in = new FileInputStream( settingsPath.toString() );
try
{
context = JAXBContext.newInstance( Settings.class );
Unmarshaller m = context.createUnmarshaller();
mSettings = (Settings)m.unmarshal( in );
}
catch ( JAXBException e )
{
mLog.error( "jaxb exception while loading " +
"settings", e );
}
}
catch ( Exception e )
{
mLog.error( "coulcn't open inputstream to " +
"load settings [" + settingsPath.toString() + "]", e );
}
finally
{
if( in != null )
{
try
{
in.close();
}
catch ( IOException e )
{
mLog.error( "exception while closing " +
"the settings file inputstream reader", e );
}
}
}
}
else
{
mLog.info( "settings does not exist [" + settingsPath.toString() + "]" );
}
if( mSettings == null )
{
mSettings = new Settings();
}
}
public void broadcastSettingChange( Setting setting )
{
Iterator it = mListeners.iterator();
while( it.hasNext() )
{
SettingChangeListener listener = it.next();
if( listener == null )
{
it.remove();
}
else
{
listener.settingChanged( setting );
}
}
}
public void broadcastSettingDeleted( Setting setting )
{
Iterator it = mListeners.iterator();
while( it.hasNext() )
{
SettingChangeListener listener = it.next();
if( listener == null )
{
it.remove();
}
else
{
listener.settingDeleted( setting );
}
}
}
public void addListener( SettingChangeListener listener )
{
mListeners.add( listener );
}
public void removeListener( SettingChangeListener listener )
{
mListeners.remove( listener );
}
}