#1060 Adds Alias List delete and rename operations to playlist editor (#2038)

Original code changes implemented by @whelgeson

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2024-10-21 05:34:06 -04:00
committed by GitHub
parent 378841b408
commit 66a0b2543b
6 changed files with 378 additions and 37 deletions

View File

@@ -0,0 +1,37 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.gui.playlist;
/**
* Interface for editors that use alias list controls that can trigger when the playlist manager updates the observable
* set of alias list names and cause the editor to prompt the user to save changes.
*
* Implementors should clear any currently selected/editing element that could be impacted by a behind-the-scenes update
* to the list of alias list names such as delete or rename operations.
*/
public interface IAliasListRefreshListener
{
/**
* Preparate foran alias list refresh. For alias editors, this can be a clear the currently selected
* alias so that if the value is changed, the editor doesn't think that the user has modified the alias and
* prompt the user to save the changes.
*/
void prepareForAliasListRefresh();
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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
@@ -25,6 +25,7 @@ import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.gui.control.MaxLengthUnaryOperator;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.gui.playlist.IAliasListRefreshListener;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
@@ -41,7 +42,6 @@ import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
@@ -76,7 +76,7 @@ import org.slf4j.LoggerFactory;
/**
* Editor for aliases
*/
public class AliasConfigurationEditor extends SplitPane
public class AliasConfigurationEditor extends SplitPane implements IAliasListRefreshListener
{
private static final Logger mLog = LoggerFactory.getLogger(AliasConfigurationEditor.class);
@@ -89,6 +89,7 @@ public class AliasConfigurationEditor extends SplitPane
private Label mPlaceholderLabel;
private Button mNewAliasButton;
private Button mDeleteAliasButton;
private Button mRenameAliasButton;
private Button mCloneAliasButton;
private MenuButton mMoveToAliasButton;
private VBox mButtonBox;
@@ -96,13 +97,20 @@ public class AliasConfigurationEditor extends SplitPane
private TextField mSearchField;
private ComboBox<String> mAliasListNameComboBox;
private Button mNewAliasListButton;
private Button mDeleteAliasListButton;
private FilteredList<Alias> mAliasFilteredList;
private SortedList<Alias> mAliasSortedList;
private AliasPredicate mAliasPredicate;
/**
* Constructs an instance
* @param playlistManager for playlist operations
* @param userPreferences for user preferences
*/
public AliasConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences)
{
mPlaylistManager = playlistManager;
mPlaylistManager.addAliasListRefreshListener(this);
mUserPreferences = userPreferences;
VBox leftBox = new VBox();
@@ -118,6 +126,15 @@ public class AliasConfigurationEditor extends SplitPane
getItems().addAll(topBox, getAliasItemEditor());
}
/**
* Prepares for an alias list refresh by clearing the currently selected alias item from the editor.
*/
@Override
public void prepareForAliasListRefresh()
{
getAliasTableView().getSelectionModel().select(null);
}
/**
* Request to show the specified alias in the editor.
* <p>
@@ -167,18 +184,17 @@ public class AliasConfigurationEditor extends SplitPane
alert.setTitle("Save Changes");
alert.setHeaderText("Alias configuration has been modified");
alert.setContentText("Do you want to save these changes?");
alert.initOwner(((Node) getButtonBox()).getScene().getWindow());
alert.initOwner((getButtonBox()).getScene().getWindow());
//Workaround for JavaFX KDE on Linux bug in FX 10/11: https://bugs.openjdk.java.net/browse/JDK-8179073
alert.setResizable(true);
alert.onShownProperty().addListener(e ->
{
alert.onShownProperty().addListener(e -> {
Platform.runLater(() -> alert.setResizable(false));
});
Optional<ButtonType> result = alert.showAndWait();
if(result.get() == ButtonType.YES)
if(result.isPresent() && result.get() == ButtonType.YES)
{
getAliasItemEditor().save();
}
@@ -202,7 +218,7 @@ public class AliasConfigurationEditor extends SplitPane
getAliasBulkEditor().setItem(aliases);
}
getCloneAliasButton().setDisable(aliases.isEmpty() || aliases.size() > 1);
getCloneAliasButton().setDisable(aliases.size() != 1);
getDeleteAliasButton().setDisable(aliases.isEmpty());
getMoveToAliasButton().setDisable(aliases.isEmpty());
}
@@ -248,7 +264,7 @@ public class AliasConfigurationEditor extends SplitPane
searchBox.setAlignment(Pos.BASELINE_RIGHT);
mSearchAndListSelectionBox.getChildren().addAll(listLabel, getAliasListNameComboBox(),
getNewAliasListButton(), searchBox);
getNewAliasListButton(), getRenameAliasListButton(), getDeleteAliasListButton(), searchBox);
}
return mSearchAndListSelectionBox;
@@ -347,6 +363,70 @@ public class AliasConfigurationEditor extends SplitPane
return mNewAliasListButton;
}
private Button getRenameAliasListButton() {
if (mRenameAliasButton == null) {
mRenameAliasButton = new Button("Rename Alias List");
mRenameAliasButton.setOnAction(event -> {
String aliasListName = getAliasListNameComboBox().getSelectionModel().getSelectedItem();
if (aliasListName.equals(AliasModel.NO_ALIAS_LIST)) {
Alert alert = new Alert(Alert.AlertType.ERROR, "", ButtonType.OK);
alert.setTitle("Rename Alias List");
alert.setHeaderText("You cannot rename " + aliasListName + ".");
alert.initOwner((getRenameAliasListButton()).getScene().getWindow());
alert.showAndWait();
return;
}
TextInputDialog dialog = new TextInputDialog();
dialog.setTitle("Renaming Alias List: " + aliasListName);
dialog.setHeaderText("Please enter the new alias list name (max 25 chars).");
dialog.setContentText("Name:");
dialog.getEditor().setTextFormatter(new TextFormatter<String>(new MaxLengthUnaryOperator(25)));
Optional<String> result = dialog.showAndWait();
result.ifPresent(newAliasListName -> {
mPlaylistManager.renameAliasList(aliasListName, newAliasListName);
getAliasListNameComboBox().getSelectionModel().select(newAliasListName);
});
});
}
return mRenameAliasButton;
}
private Button getDeleteAliasListButton() {
if (mDeleteAliasListButton == null) {
mDeleteAliasListButton = new Button("Delete Alias List");
mDeleteAliasListButton.setOnAction(event -> {
String aliasListName = getAliasListNameComboBox().getSelectionModel().getSelectedItem();
if (aliasListName.equals(AliasModel.NO_ALIAS_LIST)) {
Alert alert = new Alert(Alert.AlertType.ERROR, "", ButtonType.OK);
alert.setTitle("Delete Alias List");
alert.setHeaderText("You cannot delete " + aliasListName + ".");
alert.initOwner((getDeleteAliasListButton()).getScene().getWindow());
alert.showAndWait();
return;
}
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "", ButtonType.NO, ButtonType.YES);
alert.setTitle("Delete Alias List");
alert.setHeaderText("Are you sure you want to delete the alias list " + aliasListName + " and all associated aliases?");
alert.initOwner((getDeleteAliasListButton()).getScene().getWindow());
Optional<ButtonType> result = alert.showAndWait();
if (result.isPresent() && result.get() == ButtonType.YES)
{
mPlaylistManager.deleteAliasList(aliasListName);
}
});
}
return mDeleteAliasListButton;
}
private TableView<Alias> getAliasTableView()
{
if(mAliasTableView == null)
@@ -423,8 +503,7 @@ public class AliasConfigurationEditor extends SplitPane
mAliasTableView.setPlaceholder(getPlaceholderLabel());
mAliasTableView.setItems(getAliasSortedList());
mAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
mAliasTableView.getSelectionModel().getSelectedItems().addListener((ListChangeListener<Alias>) c ->
{
mAliasTableView.getSelectionModel().getSelectedItems().addListener((ListChangeListener<Alias>)c -> {
Platform.runLater(() -> setAliases(mAliasTableView.getSelectionModel().getSelectedItems()));
});
}
@@ -537,11 +616,11 @@ public class AliasConfigurationEditor extends SplitPane
ButtonType.NO, ButtonType.YES);
alert.setTitle("Delete Alias");
alert.setHeaderText("Are you sure?");
alert.initOwner(((Node) getDeleteAliasButton()).getScene().getWindow());
alert.initOwner((getDeleteAliasButton()).getScene().getWindow());
Optional<ButtonType> result = alert.showAndWait();
if(result.get() == ButtonType.YES)
if(result.isPresent() && result.get() == ButtonType.YES)
{
List<Alias> selectedAliases = new ArrayList<>(getAliasTableView().getSelectionModel().getSelectedItems());
mPlaylistManager.getAliasModel().removeAliases(selectedAliases);

View File

@@ -21,6 +21,7 @@ package io.github.dsheirer.gui.playlist.channel;
import com.google.common.base.Joiner;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.gui.playlist.IAliasListRefreshListener;
import io.github.dsheirer.module.decode.DecoderFactory;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.playlist.PlaylistManager;
@@ -73,7 +74,7 @@ import org.slf4j.LoggerFactory;
/**
* JavaFX editor for managing channel configurations.
*/
public class ChannelEditor extends SplitPane implements IFilterProcessor
public class ChannelEditor extends SplitPane implements IFilterProcessor, IAliasListRefreshListener
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelEditor.class);
private PlaylistManager mPlaylistManager;
@@ -104,6 +105,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
public ChannelEditor(PlaylistManager playlistManager, TunerManager tunerManager, UserPreferences userPreferences)
{
mPlaylistManager = playlistManager;
mPlaylistManager.addAliasListRefreshListener(this);
mTunerManager = tunerManager;
mUserPreferences = userPreferences;
mUnknownConfigurationEditor = new UnknownConfigurationEditor(mPlaylistManager, mTunerManager,
@@ -124,6 +126,15 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
getItems().addAll(topBox, getChannelConfigurationEditor());
}
/**
* Prepares for an alias list refresh by clearing the currently selected channel from the channel table.
*/
@Override
public void prepareForAliasListRefresh()
{
getChannelTableView().getSelectionModel().select(null);
}
/**
* Processes the channel view request.
*