From 34235382fbee082c4f542cafcbdfc283934efffc Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sun, 27 Jan 2019 12:05:20 +0000 Subject: [PATCH] 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. --- .../dsheirer/alias/AliasController.java | 34 ++-- .../audio/broadcast/BroadcastPanel.java | 18 +- .../audio/broadcast/BroadcastStatusPanel.java | 19 ++- .../metadata/ChannelMetadataPanel.java | 6 + ...tadataViewer.java => NowPlayingPanel.java} | 19 +-- .../dsheirer/controller/ControllerPanel.java | 43 ++--- .../controller/channel/ChannelController.java | 43 +++-- .../java/io/github/dsheirer/gui/SDRTrunk.java | 50 +++++- .../module/decode/event/DecodeEventPanel.java | 26 ++- .../decode/event/MessageActivityPanel.java | 29 ++-- .../dsheirer/preference/UserPreferences.java | 11 ++ .../swing/JTableColumnWidthMonitor.java | 154 ++++++++++++++++++ .../preference/swing/SwingPreference.java | 148 +++++++++++++++++ .../dsheirer/source/tuner/TunerViewPanel.java | 39 +++-- 14 files changed, 512 insertions(+), 127 deletions(-) rename src/main/java/io/github/dsheirer/channel/metadata/{ChannelMetadataViewer.java => NowPlayingPanel.java} (83%) create mode 100644 src/main/java/io/github/dsheirer/preference/swing/JTableColumnWidthMonitor.java create mode 100644 src/main/java/io/github/dsheirer/preference/swing/SwingPreference.java diff --git a/src/main/java/io/github/dsheirer/alias/AliasController.java b/src/main/java/io/github/dsheirer/alias/AliasController.java index 89851f6e..4b321d91 100644 --- a/src/main/java/io/github/dsheirer/alias/AliasController.java +++ b/src/main/java/io/github/dsheirer/alias/AliasController.java @@ -24,6 +24,8 @@ import com.jidesoft.swing.JideSplitPane; import io.github.dsheirer.audio.broadcast.BroadcastModel; import io.github.dsheirer.gui.editor.Editor; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; import net.coderazzi.filters.gui.AutoChoices; import net.coderazzi.filters.gui.TableFilterHeader; import net.miginfocom.swing.MigLayout; @@ -48,17 +50,18 @@ import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; -public class AliasController extends JPanel - implements ActionListener, ListSelectionListener +public class AliasController extends JPanel implements ActionListener, ListSelectionListener { private static final long serialVersionUID = 1L; + private static final String TABLE_PREFERENCE_KEY = "alias.controller"; private AliasModel mAliasModel; private JTable mAliasTable; private TableFilterHeader mTableFilterHeader; private AliasEditor mAliasEditor; private MultipleAliasEditor mMultipleAliasEditor; private JideSplitPane mSplitPane; + private UserPreferences mUserPreferences; private static final String NEW_ALIAS = "New"; private static final String COPY_ALIAS = "Copy"; @@ -69,15 +72,16 @@ public class AliasController extends JPanel private JButton mDeleteButton = new JButton(DELETE_ALIAS); private IconCellRenderer mIconCellRenderer; + private JTableColumnWidthMonitor mColumnWidthMonitor; - public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager) + public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager, + UserPreferences userPreferences) { mAliasModel = aliasModel; - mAliasEditor = new AliasEditor(mAliasModel, broadcastModel, iconManager); mMultipleAliasEditor = new MultipleAliasEditor(mAliasModel, broadcastModel, iconManager); - mIconCellRenderer = new IconCellRenderer(iconManager); + mUserPreferences = userPreferences; init(); @@ -86,9 +90,7 @@ public class AliasController extends JPanel private void init() { - setLayout(new MigLayout("insets 0 0 0 0", - "[grow,fill]", - "[grow,fill]")); + setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); //System Configuration View and Editor mAliasTable = new JTable(mAliasModel); @@ -96,11 +98,11 @@ public class AliasController extends JPanel mAliasTable.getSelectionModel().addListSelectionListener(this); mAliasTable.setAutoCreateRowSorter(true); - mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR) - .setCellRenderer(new ColorCellRenderer()); + mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR).setCellRenderer(new ColorCellRenderer()); - mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON) - .setCellRenderer(mIconCellRenderer); + mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON).setCellRenderer(mIconCellRenderer); + + mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mAliasTable, TABLE_PREFERENCE_KEY); mTableFilterHeader = new TableFilterHeader(mAliasTable, AutoChoices.ENABLED); mTableFilterHeader.setFilterOnUpdates(true); @@ -109,8 +111,8 @@ public class AliasController extends JPanel JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout( - new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]")); + buttonsPanel.setLayout(new MigLayout("insets 0 0 0 0", + "[grow,fill][grow,fill][grow,fill]", "[]")); mNewButton.addActionListener(this); mNewButton.setToolTipText("Adds a new alias"); @@ -128,8 +130,8 @@ public class AliasController extends JPanel JPanel listAndButtonsPanel = new JPanel(); - listAndButtonsPanel.setLayout( - new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]")); + listAndButtonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", + "[grow,fill][]")); listAndButtonsPanel.add(tableScroller, "wrap"); listAndButtonsPanel.add(buttonsPanel); diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastPanel.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastPanel.java index 74eeb3cc..e1b8a9f1 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastPanel.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer + * 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 @@ -14,8 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.audio.broadcast; import com.jidesoft.swing.JideSplitPane; @@ -23,6 +24,7 @@ import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.gui.editor.Editor; import io.github.dsheirer.gui.editor.EmptyEditor; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.preference.UserPreferences; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,6 +53,7 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect private BroadcastModel mBroadcastModel; private AliasModel mAliasModel; private IconManager mIconManager; + private UserPreferences mUserPreferences; private BroadcastStatusPanel mBroadcastStatusPanel; private JideSplitPane mSplitPane; @@ -60,12 +63,14 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect private JButton mCopyButton = new JButton(COPY_BROADCAST_CONFIGURATION); private JButton mDeleteButton = new JButton(DELETE_BROADCAST_CONFIGURATION); - public BroadcastPanel(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager) + public BroadcastPanel(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager, + UserPreferences userPreferences) { mBroadcastModel = broadcastModel; mAliasModel = aliasModel; mIconManager = iconManager; mEditor = mEmptyEditor; + mUserPreferences = userPreferences; init(); } @@ -74,7 +79,8 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect { setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]")); - mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel); + mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel, mUserPreferences, + "broadcast.panel.broadcast.status.panel"); mBroadcastStatusPanel.getTable().setAutoCreateRowSorter(true); mBroadcastStatusPanel.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastStatusPanel.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastStatusPanel.java index 38ce234e..429a3ded 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastStatusPanel.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastStatusPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,10 +15,12 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.audio.broadcast; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; import net.miginfocom.swing.MigLayout; import javax.swing.JLabel; @@ -32,12 +35,17 @@ import java.awt.Component; public class BroadcastStatusPanel extends JPanel { private JTable mTable; + private JTableColumnWidthMonitor mColumnWidthMonitor; private JScrollPane mScrollPane; private BroadcastModel mBroadcastModel; + private UserPreferences mUserPreferences; + private String mPreferenceKey; - public BroadcastStatusPanel(BroadcastModel broadcastModel) + public BroadcastStatusPanel(BroadcastModel broadcastModel, UserPreferences userPreferences, String preferenceKey) { mBroadcastModel = broadcastModel; + mUserPreferences = userPreferences; + mPreferenceKey = preferenceKey; init(); } @@ -57,6 +65,7 @@ public class BroadcastStatusPanel extends JPanel renderer.setHorizontalAlignment(SwingConstants.CENTER); mTable.getColumnModel().getColumn(BroadcastModel.COLUMN_BROADCASTER_STATUS).setCellRenderer(new StatusCellRenderer()); + mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, mPreferenceKey); mScrollPane = new JScrollPane(mTable); diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java index 5c64d920..30da5070 100644 --- a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java +++ b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java @@ -33,6 +33,7 @@ import io.github.dsheirer.identifier.decoder.ChannelStateIdentifier; import io.github.dsheirer.module.ProcessingChain; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference; +import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; import net.miginfocom.swing.MigLayout; @@ -58,6 +59,7 @@ import java.util.Map; public class ChannelMetadataPanel extends JPanel implements ListSelectionListener { + private static final String TABLE_PREFERENCE_KEY = "channel.metadata.panel"; private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; private IconManager mIconManager; @@ -66,6 +68,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene private Broadcaster mSelectedProcessingChainBroadcaster = new Broadcaster<>(); private Map mBackgroundColors = new HashMap<>(); private Map mForegroundColors = new HashMap<>(); + private JTableColumnWidthMonitor mTableColumnMonitor; /** * Table view for currently decoding channel metadata @@ -108,6 +111,9 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_CONFIGURATION_FREQUENCY) .setCellRenderer(new FrequencyCellRenderer()); + //Add a table column width monitor to store/restore column widths + mTableColumnMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY); + JScrollPane scrollPane = new JScrollPane(mTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataViewer.java b/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java similarity index 83% rename from src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataViewer.java rename to src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java index b7b3c65c..4a434643 100644 --- a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataViewer.java +++ b/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,8 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.channel.metadata; import com.jidesoft.swing.JideSplitPane; @@ -33,10 +34,9 @@ import net.miginfocom.swing.MigLayout; import javax.swing.JPanel; import java.awt.Color; -public class ChannelMetadataViewer extends JPanel +public class NowPlayingPanel extends JPanel { private ChannelMetadataPanel mChannelMetadataPanel; - private ChannelDetailPanel mChannelDetailPanel; private DecodeEventPanel mDecodeEventPanel; private MessageActivityPanel mMessageActivityPanel; @@ -45,12 +45,12 @@ public class ChannelMetadataViewer extends JPanel * GUI panel that combines the currently decoding channels metadata table and viewers for channel details, * messages, events, and spectral view. */ - public ChannelMetadataViewer(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, - IconManager iconManager, AliasModel aliasModel, UserPreferences userPreferences) + public NowPlayingPanel(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, + IconManager iconManager, AliasModel aliasModel, UserPreferences userPreferences) { mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager); mDecodeEventPanel = new DecodeEventPanel(iconManager, userPreferences, aliasModel); - mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager); + mMessageActivityPanel = new MessageActivityPanel(userPreferences); mChannelMetadataPanel = new ChannelMetadataPanel(channelModel, channelProcessingManager, iconManager, userPreferences); init(); @@ -64,7 +64,6 @@ public class ChannelMetadataViewer extends JPanel tabbedPane.addTab("Details", mChannelDetailPanel); tabbedPane.addTab("Events", mDecodeEventPanel); tabbedPane.addTab("Messages", mMessageActivityPanel); -// tabbedPane.addTab("Spectrum", mChannelSpectrumPanel); tabbedPane.setFont(this.getFont()); tabbedPane.setForeground(Color.BLACK); diff --git a/src/main/java/io/github/dsheirer/controller/ControllerPanel.java b/src/main/java/io/github/dsheirer/controller/ControllerPanel.java index 64f0eb32..3d8fb5f3 100644 --- a/src/main/java/io/github/dsheirer/controller/ControllerPanel.java +++ b/src/main/java/io/github/dsheirer/controller/ControllerPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,8 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.controller; import com.jidesoft.swing.JideTabbedPane; @@ -25,7 +26,7 @@ import io.github.dsheirer.audio.broadcast.BroadcastModel; import io.github.dsheirer.audio.broadcast.BroadcastPanel; import io.github.dsheirer.audio.playback.AudioPanel; import io.github.dsheirer.audio.playback.AudioPlaybackManager; -import io.github.dsheirer.channel.metadata.ChannelMetadataViewer; +import io.github.dsheirer.channel.metadata.NowPlayingPanel; import io.github.dsheirer.controller.channel.ChannelController; import io.github.dsheirer.controller.channel.ChannelModel; import io.github.dsheirer.controller.channel.ChannelProcessingManager; @@ -41,7 +42,6 @@ import io.github.dsheirer.source.tuner.TunerViewPanel; import net.miginfocom.swing.MigLayout; import javax.swing.JPanel; -import javax.swing.JTable; import java.awt.Color; import java.awt.Dimension; @@ -53,27 +53,18 @@ public class ControllerPanel extends JPanel private AudioPanel mAudioPanel; private BroadcastPanel mBroadcastPanel; private ChannelController mChannelController; - private ChannelMetadataViewer mChannelMetadataViewer; + private NowPlayingPanel mNowPlayingPanel; private ChannelModel mChannelModel; private MapPanel mMapPanel; private TunerViewPanel mTunerManagerPanel; private BroadcastModel mBroadcastModel; private JideTabbedPane mTabbedPane; - protected JTable mChannelActivityTable = new JTable(); - public ControllerPanel(AudioPlaybackManager audioPlaybackManager, - AliasModel aliasModel, - BroadcastModel broadcastModel, - ChannelModel channelModel, - ChannelMapModel channelMapModel, - ChannelProcessingManager channelProcessingManager, - IconManager iconManager, - MapService mapService, - SettingsManager settingsManager, - SourceManager sourceManager, - TunerModel tunerModel, - UserPreferences userPreferences) + public ControllerPanel(AudioPlaybackManager audioPlaybackManager, AliasModel aliasModel, BroadcastModel broadcastModel, + ChannelModel channelModel, ChannelMapModel channelMapModel, ChannelProcessingManager channelProcessingManager, + IconManager iconManager, MapService mapService, SettingsManager settingsManager, + SourceManager sourceManager, TunerModel tunerModel, UserPreferences userPreferences) { mBroadcastModel = broadcastModel; mChannelModel = channelModel; @@ -81,19 +72,19 @@ public class ControllerPanel extends JPanel mAudioPanel = new AudioPanel(iconManager, userPreferences, settingsManager, sourceManager, audioPlaybackManager, aliasModel); - mChannelMetadataViewer = new ChannelMetadataViewer(channelModel, channelProcessingManager, iconManager, + mNowPlayingPanel = new NowPlayingPanel(channelModel, channelProcessingManager, iconManager, aliasModel, userPreferences); mMapPanel = new MapPanel(mapService, aliasModel, iconManager, settingsManager); - mBroadcastPanel = new BroadcastPanel(broadcastModel, aliasModel, iconManager); + mBroadcastPanel = new BroadcastPanel(broadcastModel, aliasModel, iconManager, userPreferences); mChannelController = new ChannelController(channelModel, channelProcessingManager, channelMapModel, - sourceManager, aliasModel); + sourceManager, aliasModel, userPreferences); - mAliasController = new AliasController(aliasModel, broadcastModel, iconManager); + mAliasController = new AliasController(aliasModel, broadcastModel, iconManager, userPreferences); - mTunerManagerPanel = new TunerViewPanel(tunerModel); + mTunerManagerPanel = new TunerViewPanel(tunerModel, userPreferences); init(); } @@ -108,7 +99,7 @@ public class ControllerPanel extends JPanel mTabbedPane = new JideTabbedPane(); mTabbedPane.setFont(this.getFont()); mTabbedPane.setForeground(Color.BLACK); - mTabbedPane.addTab("Now Playing", mChannelMetadataViewer); + mTabbedPane.addTab("Now Playing", mNowPlayingPanel); mTabbedPane.addTab("Aliases", mAliasController); mTabbedPane.addTab("Channels", mChannelController); mTabbedPane.addTab("Map", mMapPanel); diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelController.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelController.java index 9963ea7f..c99dc77a 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelController.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelController.java @@ -1,23 +1,29 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.controller.channel; import com.jidesoft.swing.JideSplitPane; import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.controller.channel.map.ChannelMapModel; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; import io.github.dsheirer.source.SourceManager; import net.coderazzi.filters.gui.AutoChoices; import net.coderazzi.filters.gui.TableFilterHeader; @@ -43,11 +49,14 @@ public class ChannelController extends JPanel implements ActionListener, ListSel { private static final long serialVersionUID = 1L; + private static final String TABLE_PREFERENCE_KEY = "channel.controller"; private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; private JTable mChannelTable; + private JTableColumnWidthMonitor mColumnWidthMonitor; private TableFilterHeader mTableFilterHeader; private ChannelEditor mEditor; + private UserPreferences mUserPreferences; private static final String NEW_CHANNEL = "New"; private static final String COPY_CHANNEL = "Copy"; @@ -57,17 +66,16 @@ public class ChannelController extends JPanel implements ActionListener, ListSel private JButton mCopyChannelButton = new JButton(COPY_CHANNEL); private JButton mDeleteChannelButton = new JButton(DELETE_CHANNEL); - public ChannelController(ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - ChannelMapModel channelMapModel, - SourceManager sourceManager, - AliasModel aliasModel) + public ChannelController(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, + ChannelMapModel channelMapModel, SourceManager sourceManager, AliasModel aliasModel, + UserPreferences userPreferences) { mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; mEditor = new ChannelEditor(channelModel, channelProcessingManager, channelMapModel, sourceManager, aliasModel); mChannelModel.addListener(mEditor); mChannelProcessingManager.addChannelEventListener(mEditor); + mUserPreferences = userPreferences; init(); } @@ -82,6 +90,7 @@ public class ChannelController extends JPanel implements ActionListener, ListSel mChannelTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); mChannelTable.getSelectionModel().addListSelectionListener(this); mChannelTable.setAutoCreateRowSorter(true); + mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mChannelTable, TABLE_PREFERENCE_KEY); mTableFilterHeader = new TableFilterHeader(mChannelTable, AutoChoices.ENABLED); mTableFilterHeader.setFilterOnUpdates(true); diff --git a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java index 35e8bbdc..fcf63d8f 100644 --- a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java +++ b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java @@ -78,6 +78,7 @@ import java.awt.AWTException; import java.awt.Desktop; import java.awt.Dimension; import java.awt.EventQueue; +import java.awt.Point; import java.awt.Robot; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -97,8 +98,12 @@ public class SDRTrunk implements Listener private final static Logger mLog = LoggerFactory.getLogger(SDRTrunk.class); private static final String PROPERTY_BROADCAST_STATUS_VISIBLE = "main.broadcast.status.visible"; - private boolean mBroadcastStatusVisible; + private static final String BASE_WINDOW_NAME = "sdrtrunk.main.window"; + private static final String CONTROLLER_PANEL_IDENTIFIER = BASE_WINDOW_NAME + ".control.panel"; + private static final String SPECTRAL_PANEL_IDENTIFIER = BASE_WINDOW_NAME + ".spectral.panel"; + private static final String WINDOW_FRAME_IDENTIFIER = BASE_WINDOW_NAME + ".frame"; + private boolean mBroadcastStatusVisible; private AudioPacketManager mAudioPacketManager; private IconManager mIconManager; private BroadcastStatusPanel mBroadcastStatusPanel; @@ -271,15 +276,45 @@ public class SDRTrunk implements Listener */ mTitle = SystemProperties.getInstance().getApplicationName(); mMainGui.setTitle(mTitle); - mMainGui.setSize(new Dimension(1280, 800)); - mMainGui.setLocationRelativeTo(null); + + Point location = mUserPreferences.getSwingPreference().getLocation(WINDOW_FRAME_IDENTIFIER); + if(location != null) + { + mMainGui.setLocation(location); + } + else + { + mMainGui.setLocationRelativeTo(null); + } mMainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mMainGui.addWindowListener(new ShutdownMonitor()); - //Set preferred sizes to influence the split + Dimension dimension = mUserPreferences.getSwingPreference().getDimension(WINDOW_FRAME_IDENTIFIER); + mSpectralPanel.setPreferredSize(new Dimension(1280, 300)); mControllerPanel.setPreferredSize(new Dimension(1280, 500)); + if(dimension != null) + { + Dimension spectral = mUserPreferences.getSwingPreference().getDimension(SPECTRAL_PANEL_IDENTIFIER); + if(spectral != null) + { + mSpectralPanel.setSize(spectral); + } + + Dimension controller = mUserPreferences.getSwingPreference().getDimension(CONTROLLER_PANEL_IDENTIFIER); + if(controller != null) + { + mControllerPanel.setSize(controller); + } + + mMainGui.setSize(dimension); + } + else + { + mMainGui.setSize(new Dimension(1280, 800)); + } + mSplitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT); mSplitPane.setDividerSize(5); mSplitPane.add(mSpectralPanel); @@ -424,6 +459,10 @@ public class SDRTrunk implements Listener private void processShutdown() { mLog.info("Application shutdown started ..."); + mUserPreferences.getSwingPreference().setLocation(WINDOW_FRAME_IDENTIFIER, mMainGui.getLocation()); + mUserPreferences.getSwingPreference().setDimension(WINDOW_FRAME_IDENTIFIER, mMainGui.getSize()); + mUserPreferences.getSwingPreference().setDimension(SPECTRAL_PANEL_IDENTIFIER, mSpectralPanel.getSize()); + mUserPreferences.getSwingPreference().setDimension(CONTROLLER_PANEL_IDENTIFIER, mControllerPanel.getSize()); mJavaFxWindowManager.shutdown(); mLog.info("Stopping channels ..."); mChannelProcessingManager.shutdown(); @@ -443,7 +482,8 @@ public class SDRTrunk implements Listener { if(mBroadcastStatusPanel == null) { - mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel); + mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel, mUserPreferences, + "application.broadcast.status.panel"); mBroadcastStatusPanel.setPreferredSize(new Dimension(880, 70)); mBroadcastStatusPanel.getTable().setEnabled(false); } diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java index 22326b8b..dab408e0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java @@ -1,7 +1,7 @@ /* * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2018 Dennis Sheirer + * 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 @@ -34,6 +34,7 @@ import io.github.dsheirer.identifier.Role; import io.github.dsheirer.module.ProcessingChain; import io.github.dsheirer.preference.PreferenceType; import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; import io.github.dsheirer.sample.Listener; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -55,10 +56,12 @@ import java.util.List; public class DecodeEventPanel extends JPanel implements Listener { - private final static Logger mLog = LoggerFactory.getLogger(DecodeEventPanel.class); - private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger(DecodeEventPanel.class); + private static final String TABLE_PREFERENCE_KEY = "decode.event.panel"; + private JTable mTable; + private JTableColumnWidthMonitor mTableColumnWidthMonitor; private DecodeEventModel mEmptyDecodeEventModel; private JScrollPane mEmptyScroller; private IconManager mIconManager; @@ -83,6 +86,7 @@ public class DecodeEventPanel extends JPanel implements Listener { + + //Dispose the current column width monitor and recreate after we swap out the table model + mTableColumnWidthMonitor.dispose(); mTable.setModel(processingChain != null ? processingChain.getDecodeEventModel() : mEmptyDecodeEventModel); if(processingChain != null) { updateCellRenderers(); } + + mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY); }); } @@ -162,17 +171,6 @@ public class DecodeEventPanel extends JPanel implements Listener identifiers = ((IdentifierCollection)value).getIdentifiers(mRole); - -// if(identifiers.size() > 1) -// { -// StringBuilder sb = new StringBuilder(); -// sb.append("Multiple Identifiers:\n"); -// for(Identifier identifier: identifiers) -// { -// sb.append("ID:").append(identifier.debug()).append("\n"); -// } -// mLog.debug(sb.toString()); -// } label.setText(format(identifiers)); } else diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java index 42c89762..d25323e5 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,17 +15,18 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.event; import com.jidesoft.swing.JideButton; import com.jidesoft.swing.JideSplitButton; -import io.github.dsheirer.controller.channel.ChannelProcessingManager; import io.github.dsheirer.filter.FilterEditorPanel; import io.github.dsheirer.filter.FilterSet; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.ProcessingChain; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; import io.github.dsheirer.sample.Listener; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -49,26 +51,24 @@ import java.awt.event.MouseListener; public class MessageActivityPanel extends JPanel implements Listener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class); - + private static final String TABLE_PREFERENCE_KEY = "message.activity.panel"; private static MessageActivityModel EMPTY_MODEL = new MessageActivityModel(); - private JTable mTable = new JTable(EMPTY_MODEL); - + private JTableColumnWidthMonitor mTableColumnWidthMonitor; + private UserPreferences mUserPreferences; private MessageManagementPanel mManagementPanel = new MessageManagementPanel(); - private ChannelProcessingManager mChannelProcessingManager; - - public MessageActivityPanel(ChannelProcessingManager channelProcessingManager) + public MessageActivityPanel(UserPreferences userPreferences) { - mChannelProcessingManager = channelProcessingManager; + mUserPreferences = userPreferences; setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]")); add(mManagementPanel, "span,growx"); add(new JScrollPane(mTable), "span,grow"); + mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY); } @Override @@ -79,6 +79,8 @@ public class MessageActivityPanel extends JPanel implements Listener 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 return mTunerPreference; } + + /** + * Swing window location/size user preferences + */ + public SwingPreference getSwingPreference() + { + return mSwingPreference; + } + /** * Loads the managed preferences */ diff --git a/src/main/java/io/github/dsheirer/preference/swing/JTableColumnWidthMonitor.java b/src/main/java/io/github/dsheirer/preference/swing/JTableColumnWidthMonitor.java new file mode 100644 index 00000000..7eeb37d4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/swing/JTableColumnWidthMonitor.java @@ -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 + * ***************************************************************************** + */ + +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); + } + } +} diff --git a/src/main/java/io/github/dsheirer/preference/swing/SwingPreference.java b/src/main/java/io/github/dsheirer/preference/swing/SwingPreference.java new file mode 100644 index 00000000..4aaf9842 --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/swing/SwingPreference.java @@ -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 + * ***************************************************************************** + */ + +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; + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java index 217b62c0..35950965 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java @@ -1,24 +1,28 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer +/* + * ****************************************************************************** + * 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 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. + * 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 - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.source.tuner; import com.jidesoft.swing.JideSplitPane; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.tuner.TunerEvent.Event; import net.miginfocom.swing.MigLayout; @@ -49,17 +53,21 @@ public class TunerViewPanel extends JPanel { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger(TunerViewPanel.class); + private static final String TABLE_PREFERENCE_KEY = "tuner.view.panel"; private TunerModel mTunerModel; private JTable mTunerTable; + private JTableColumnWidthMonitor mColumnWidthMonitor; private TableRowSorter mRowSorter; private JideSplitPane mSplitPane; private TunerEditor mTunerEditor; + private UserPreferences mUserPreferences; - public TunerViewPanel(TunerModel tunerModel) + public TunerViewPanel(TunerModel tunerModel, UserPreferences userPreferences) { mTunerModel = tunerModel; mTunerEditor = new TunerEditor(mTunerModel.getTunerConfigurationModel()); + mUserPreferences = userPreferences; init(); } @@ -149,6 +157,7 @@ public class TunerViewPanel extends JPanel TableCellRenderer renderer = new LinkCellRenderer(); mTunerTable.getColumnModel().getColumn(TunerModel.SPECTRAL_DISPLAY_NEW).setCellRenderer(renderer); + mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTunerTable, TABLE_PREFERENCE_KEY); JScrollPane listScroller = new JScrollPane(mTunerTable); listScroller.setPreferredSize(new Dimension(400, 20));