513 persist application layout across restarts (#514)

* Stores minimal main application window location and size/layout settings
to allow application to be restored to the same size/location on
restart.

* Adds column width monitor to all tables in application that stores the
column width settings in the user preferences so that on restarting
the application the table column widths are restored to the previous
widths.
This commit is contained in:
Denny Sheirer
2019-01-27 12:05:20 +00:00
committed by GitHub
parent c05aff0768
commit 34235382fb
14 changed files with 512 additions and 127 deletions

View File

@@ -28,6 +28,7 @@ import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
import io.github.dsheirer.preference.playlist.PlaylistPreference;
import io.github.dsheirer.preference.source.ChannelMultiFrequencyPreference;
import io.github.dsheirer.preference.source.TunerPreference;
import io.github.dsheirer.preference.swing.SwingPreference;
import io.github.dsheirer.sample.Listener;
/**
@@ -55,6 +56,7 @@ public class UserPreferences implements Listener<PreferenceType>
private PlaylistPreference mPlaylistPreference;
private TalkgroupFormatPreference mTalkgroupFormatPreference;
private TunerPreference mTunerPreference;
private SwingPreference mSwingPreference = new SwingPreference();
/**
* Constructs a new user preferences instance
@@ -120,6 +122,15 @@ public class UserPreferences implements Listener<PreferenceType>
return mTunerPreference;
}
/**
* Swing window location/size user preferences
*/
public SwingPreference getSwingPreference()
{
return mSwingPreference;
}
/**
* Loads the managed preferences
*/

View File

@@ -0,0 +1,154 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.preference.swing;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.TableColumnModel;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Monitors a JTable column model and persists column width changes to the user preferences. Restores
* previous column width preferred sizes on application restart.
*/
public class JTableColumnWidthMonitor
{
private static final Logger mLog = LoggerFactory.getLogger(JTableColumnWidthMonitor.class);
private UserPreferences mUserPreferences;
private JTable mTable;
private String mKey;
private ColumnResizeListener mColumnResizeListener = new ColumnResizeListener();
private AtomicBoolean mSaveInProgress = new AtomicBoolean();
/**
* Constructs a column width monitor.
*
* @param userPreferences to store column widths
* @param table to monitor for column width changes
* @param key that uniquely identifies the table to monitor
*/
public JTableColumnWidthMonitor(UserPreferences userPreferences, JTable table, String key)
{
mUserPreferences = userPreferences;
mTable = table;
mKey = key;
restoreColumnWidths();
mTable.getColumnModel().addColumnModelListener(mColumnResizeListener);
}
/**
* Prepares this monitor for disposal by unregistering as a listener to the table column model.
*/
public void dispose()
{
if(mTable != null && mColumnResizeListener != null)
{
mTable.getColumnModel().removeColumnModelListener(mColumnResizeListener);
}
mTable = null;
mUserPreferences = null;
}
/**
* Sets the preferred column widths on the table from persisted settings
*/
private void restoreColumnWidths()
{
TableColumnModel model = mTable.getColumnModel();
for(int x = 0; x < model.getColumnCount(); x++)
{
int width = mUserPreferences.getSwingPreference().getInt(getColumnKey(x), Integer.MAX_VALUE);
if(width != Integer.MAX_VALUE)
{
model.getColumn(x).setPreferredWidth(width);
}
}
}
/**
* Stores the current column widths to the user preferences
*/
private void storeColumnWidths()
{
TableColumnModel model = mTable.getColumnModel();
for(int x = 0; x < model.getColumnCount(); x++)
{
mUserPreferences.getSwingPreference().setInt(getColumnKey(x), model.getColumn(x).getWidth());
}
}
/**
* Constructs a preference key for the column number
*/
private String getColumnKey(int column)
{
return mKey + ".column." + column;
}
/**
* Table column model listener.
*/
class ColumnResizeListener implements TableColumnModelListener
{
@Override
public void columnMarginChanged(ChangeEvent e)
{
if(mSaveInProgress.compareAndSet(false, true))
{
ThreadPool.SCHEDULED.schedule(new ColumnWidthSaveTask(), 2, TimeUnit.SECONDS);
}
}
@Override
public void columnAdded(TableColumnModelEvent e){}
@Override
public void columnRemoved(TableColumnModelEvent e){}
@Override
public void columnMoved(TableColumnModelEvent e){}
@Override
public void columnSelectionChanged(ListSelectionEvent e){}
}
public class ColumnWidthSaveTask implements Runnable
{
@Override
public void run()
{
storeColumnWidths();
mSaveInProgress.set(false);
}
}
}

View File

@@ -0,0 +1,148 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.preference.swing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.util.prefs.Preferences;
/**
* User preference settings for Swing windows and components.
*/
public class SwingPreference
{
private static final Logger mLog = LoggerFactory.getLogger(SwingPreference.class);
private static final String LOCATION_X = ".x";
private static final String LOCATION_Y = ".y";
private static final String SIZE_HEIGHT = ".height";
private static final String SIZE_WIDTH = ".width";
private Preferences mPreferences = Preferences.userNodeForPackage(SwingPreference.class);
public SwingPreference()
{
}
/**
* Returns the stored swing related integer value
* @param key for accessing the value
* @param defaultValue to return if the value has not yet been persisted
* @return value or default value
*/
public int getInt(String key, int defaultValue)
{
return mPreferences.getInt(key, defaultValue);
}
/**
* Stores the swing related integer value
* @param key to reference the value
*/
public void setInt(String key, int value)
{
mPreferences.putInt(key, value);
}
/**
* Returns the persisted window location for the specified preference key.
* @param key identifying the window
* @return location or null
*/
public Point getLocation(String key)
{
int x = mPreferences.getInt(key + LOCATION_X, Integer.MAX_VALUE);
int y = mPreferences.getInt(key + LOCATION_Y, Integer.MAX_VALUE);
if(x != Integer.MAX_VALUE && y != Integer.MAX_VALUE)
{
Point location = new Point(x,y);
if(isValidWindowLocation(location))
{
return location;
}
}
return null;
}
/**
* Stores the current window location
* @param key to identify the window
* @param location to persist
*/
public void setLocation(String key, Point location)
{
mPreferences.putInt(key + LOCATION_X, location.x);
mPreferences.putInt(key + LOCATION_Y, location.y);
}
/**
* Returns the stored size/dimension of the window identified by the key argument.
* @param key to identify the window
* @return dimension or null
*/
public Dimension getDimension(String key)
{
int height = mPreferences.getInt(key + SIZE_HEIGHT, Integer.MAX_VALUE);
int width = mPreferences.getInt(key + SIZE_WIDTH, Integer.MAX_VALUE);
if(height != Integer.MAX_VALUE && width != Integer.MAX_VALUE)
{
return new Dimension(width, height);
}
return null;
}
public void setDimension(String key, Dimension dimension)
{
mPreferences.putInt(key + SIZE_HEIGHT, dimension.height);
mPreferences.putInt(key + SIZE_WIDTH, dimension.width);
}
/**
* Tests a stored window location to determine if it can be displayed on the currently available
* graphics display devices so that we don't position a window at a previously available location
* that is not currently available.
*
* @param location to test
* @return true if the location is displayable.
*/
private boolean isValidWindowLocation(Point location)
{
for(GraphicsDevice gd:GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices())
{
if(gd.getDefaultConfiguration().getBounds().contains(location))
{
return true;
}
}
return false;
}
}