#812 Alias Bulk Editor feature (#821)

Co-authored-by: Denny <denny@denny-desktop>
This commit is contained in:
Denny Sheirer
2020-05-18 05:11:48 -04:00
committed by GitHub
parent 3b357c4d40
commit 8167e9e9f2
3 changed files with 393 additions and 14 deletions

View File

@@ -0,0 +1,343 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.alias;
import com.google.common.collect.Ordering;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.collections.transformation.SortedList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.image.ImageView;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.util.Callback;
import org.controlsfx.control.ToggleSwitch;
import java.util.List;
/**
* Editor for multiple selected aliases providing limited options for changing attributes of multiple aliases
*/
public class AliasBulkEditor extends Editor<List<Alias>>
{
private PlaylistManager mPlaylistManager;
private Label mEditingLabel;
private ColorPicker mColorPicker;
private Button mApplyColorButton;
private ComboBox<Icon> mIconNodeComboBox;
private Button mApplyIconButton;
private ToggleSwitch mMonitorAudioToggleSwitch;
private ComboBox<Integer> mMonitorPriorityComboBox;
private Button mApplyMonitorButton;
/**
* Constructs an instance
* @param playlistManager for accessing icon manager
*/
public AliasBulkEditor(PlaylistManager playlistManager)
{
mPlaylistManager = playlistManager;
GridPane gridPane = new GridPane();
gridPane.setPadding(new Insets(10,10,10,10));
gridPane.setHgap(10);
gridPane.setVgap(10);
int row = 0;
GridPane.setConstraints(getEditingLabel(), 1, row, 2, 1);
gridPane.getChildren().add(getEditingLabel());
Label colorLabel = new Label("Color");
GridPane.setHalignment(colorLabel, HPos.RIGHT);
GridPane.setConstraints(colorLabel, 0, ++row);
gridPane.getChildren().add(colorLabel);
GridPane.setConstraints(getColorPicker(), 1, row, 3, 1);
gridPane.getChildren().add(getColorPicker());
GridPane.setConstraints(getApplyColorButton(), 4, row);
gridPane.getChildren().add(getApplyColorButton());
Label iconLabel = new Label("Icon");
GridPane.setHalignment(iconLabel, HPos.RIGHT);
GridPane.setConstraints(iconLabel, 0, ++row);
gridPane.getChildren().add(iconLabel);
GridPane.setConstraints(getIconNodeComboBox(), 1, row, 3, 1);
gridPane.getChildren().add(getIconNodeComboBox());
GridPane.setConstraints(getApplyIconButton(), 4, row);
gridPane.getChildren().add(getApplyIconButton());
Label listenLabel = new Label("Listen");
GridPane.setHalignment(listenLabel, HPos.RIGHT);
GridPane.setConstraints(listenLabel, 0, ++row);
gridPane.getChildren().add(listenLabel);
GridPane.setConstraints(getMonitorAudioToggleSwitch(), 1, row);
gridPane.getChildren().add(getMonitorAudioToggleSwitch());
Label priorityLabel = new Label("Priority");
GridPane.setHalignment(priorityLabel, HPos.RIGHT);
GridPane.setConstraints(priorityLabel, 2, row);
gridPane.getChildren().add(priorityLabel);
GridPane.setConstraints(getMonitorPriorityComboBox(), 3, row);
gridPane.getChildren().add(getMonitorPriorityComboBox());
GridPane.setConstraints(getApplyMonitorButton(), 4, row);
gridPane.getChildren().add(getApplyMonitorButton());
getChildren().add(gridPane);
}
@Override
public List<Alias> getItem()
{
return super.getItem();
}
@Override
public void setItem(List<Alias> item)
{
super.setItem(item);
getEditingLabel().setText("Editing " + item.size() + " Aliases");
}
@Override
public void save()
{
//no-op
}
@Override
public void dispose()
{
//no-op
}
private Label getEditingLabel()
{
if(mEditingLabel== null)
{
mEditingLabel = new Label("Editing 0 Aliases");
}
return mEditingLabel;
}
private ColorPicker getColorPicker()
{
if(mColorPicker == null)
{
mColorPicker = new ColorPicker(Color.BLACK);
mColorPicker.setEditable(true);
mColorPicker.setStyle("-fx-color-rect-width: 60px; -fx-color-label-visible: false;");
}
return mColorPicker;
}
private Button getApplyColorButton()
{
if(mApplyColorButton == null)
{
mApplyColorButton = new Button("Apply");
mApplyColorButton.setOnAction(event -> {
int colorValue = ColorUtil.toInteger(getColorPicker().getValue());
for(Alias alias: getItem())
{
alias.setColor(colorValue);
}
});
}
return mApplyColorButton;
}
private ComboBox<Icon> getIconNodeComboBox()
{
if(mIconNodeComboBox == null)
{
mIconNodeComboBox = new ComboBox<>();
mIconNodeComboBox.setMaxWidth(Double.MAX_VALUE);
mIconNodeComboBox.setItems(new SortedList(mPlaylistManager.getIconModel().iconsProperty(), Ordering.natural()));
mIconNodeComboBox.setCellFactory(new IconCellFactory());
mIconNodeComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) ->
{
getApplyIconButton().setDisable(newValue == null);
});
}
return mIconNodeComboBox;
}
private Button getApplyIconButton()
{
if(mApplyIconButton == null)
{
mApplyIconButton = new Button("Apply");
mApplyIconButton.setDisable(true);
mApplyIconButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
Icon icon = getIconNodeComboBox().getSelectionModel().getSelectedItem();
if(icon != null)
{
for(Alias alias: getItem())
{
alias.setIconName(icon.getName());
}
}
}
});
}
return mApplyIconButton;
}
private ToggleSwitch getMonitorAudioToggleSwitch()
{
if(mMonitorAudioToggleSwitch == null)
{
mMonitorAudioToggleSwitch = new ToggleSwitch();
mMonitorAudioToggleSwitch.setSelected(true);
mMonitorAudioToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
return mMonitorAudioToggleSwitch;
}
private ComboBox<Integer> getMonitorPriorityComboBox()
{
if(mMonitorPriorityComboBox == null)
{
mMonitorPriorityComboBox = new ComboBox<>();
mMonitorPriorityComboBox.getItems().add(null);
for(int x = io.github.dsheirer.alias.id.priority.Priority.MIN_PRIORITY;
x < io.github.dsheirer.alias.id.priority.Priority.MAX_PRIORITY; x++)
{
mMonitorPriorityComboBox.getItems().add(x);
}
mMonitorPriorityComboBox.disableProperty().bind(getMonitorAudioToggleSwitch().selectedProperty().not());
mMonitorPriorityComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
return mMonitorPriorityComboBox;
}
private Button getApplyMonitorButton()
{
if(mApplyMonitorButton == null)
{
mApplyMonitorButton = new Button("Apply");
mApplyMonitorButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
boolean canMonitor = getMonitorAudioToggleSwitch().isSelected();
Integer priority = getMonitorPriorityComboBox().getSelectionModel().getSelectedItem();
if(canMonitor)
{
if(priority == null)
{
priority = io.github.dsheirer.alias.id.priority.Priority.DEFAULT_PRIORITY;
}
}
else
{
priority = io.github.dsheirer.alias.id.priority.Priority.DO_NOT_MONITOR;
}
for(Alias alias: getItem())
{
alias.setCallPriority(priority);
}
}
});
}
return mApplyMonitorButton;
}
/**
* Cell factory for combo box for dislaying icon name and graphic
*/
public class IconCellFactory implements Callback<ListView<Icon>, ListCell<Icon>>
{
@Override
public ListCell<Icon> call(ListView<Icon> param)
{
Label iconLabel = new Label();
Label textLabel = new Label();
GridPane gridPane = new GridPane();
gridPane.setHgap(5);
GridPane.setHalignment(iconLabel, HPos.RIGHT);
gridPane.getColumnConstraints().add(new ColumnConstraints(50));
gridPane.add(iconLabel, 0, 0);
gridPane.add(textLabel,1,0);
ListCell<Icon> cell = new ListCell<>()
{
@Override
protected void updateItem(Icon item, boolean empty)
{
super.updateItem(item, empty);
if(empty)
{
setText(null);
setGraphic(null);
}
else
{
textLabel.setText(item.getName());
iconLabel.setGraphic(new ImageView(item.getFxImage()));
setGraphic(gridPane);
}
}
};
return cell;
}
}
}

View File

@@ -27,12 +27,14 @@ import io.github.dsheirer.alias.AliasFactory;
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.icon.Icon;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
@@ -85,6 +87,8 @@ public class AliasConfigurationEditor extends SplitPane
private PlaylistManager mPlaylistManager;
private UserPreferences mUserPreferences;
private AliasItemEditor mAliasItemEditor;
private AliasBulkEditor mAliasBulkEditor;
private Editor mCurrentEditor;
private TableView<Alias> mAliasTableView;
private Label mPlaceholderLabel;
private Button mNewAliasButton;
@@ -114,6 +118,7 @@ public class AliasConfigurationEditor extends SplitPane
topBox.getChildren().addAll(leftBox, getButtonBox());
setOrientation(Orientation.VERTICAL);
mCurrentEditor = getAliasItemEditor();
getItems().addAll(topBox, getAliasItemEditor());
}
@@ -141,7 +146,20 @@ public class AliasConfigurationEditor extends SplitPane
}
}
private void setAlias(Alias alias)
/**
* Sets the editor as the bottom alias editor, either single alias or bulk alias editor.
*/
private void setEditor(Editor editor)
{
if(editor != mCurrentEditor)
{
getItems().remove(mCurrentEditor);
mCurrentEditor = editor;
getItems().add(mCurrentEditor);
}
}
private void setAliases(List<Alias> aliases)
{
//Prompt the user to save if the contents of the current channel editor have been modified
if(getAliasItemEditor().modifiedProperty().get())
@@ -168,18 +186,27 @@ public class AliasConfigurationEditor extends SplitPane
}
}
getCloneAliasButton().setDisable(alias == null || getAliasTableView().getSelectionModel().getSelectedItems().size() > 1);
getDeleteAliasButton().setDisable(alias == null);
getMoveToAliasButton().setDisable(alias == null);
if(getAliasTableView().getSelectionModel().getSelectedItems().size() <= 1)
if(aliases.size() <= 1)
{
getAliasItemEditor().setItem(alias);
setEditor(getAliasItemEditor());
if(aliases.size() == 1)
{
getAliasItemEditor().setItem(aliases.get(0));
}
else
{
getAliasItemEditor().setItem(null);
}
}
else
{
getAliasItemEditor().setItem(null);
setEditor(getAliasBulkEditor());
getAliasBulkEditor().setItem(aliases);
}
getCloneAliasButton().setDisable(aliases.isEmpty() || aliases.size() > 1);
getDeleteAliasButton().setDisable(aliases.isEmpty());
getMoveToAliasButton().setDisable(aliases.isEmpty());
}
private AliasItemEditor getAliasItemEditor()
@@ -192,6 +219,16 @@ public class AliasConfigurationEditor extends SplitPane
return mAliasItemEditor;
}
private AliasBulkEditor getAliasBulkEditor()
{
if(mAliasBulkEditor == null)
{
mAliasBulkEditor = new AliasBulkEditor(mPlaylistManager);
}
return mAliasBulkEditor;
}
private HBox getSearchAndListSelectionBox()
{
if(mSearchAndListSelectionBox == null)
@@ -384,10 +421,9 @@ public class AliasConfigurationEditor extends SplitPane
mAliasTableView.setPlaceholder(getPlaceholderLabel());
mAliasTableView.setItems(getAliasSortedList());
mAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
mAliasTableView.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> {
setAlias(newValue);
});
mAliasTableView.getSelectionModel().getSelectedItems().addListener((ListChangeListener<Alias>)c -> {
setAliases(mAliasTableView.getSelectionModel().getSelectedItems());
});
}
return mAliasTableView;

View File

@@ -335,12 +335,12 @@ public class AliasItemEditor extends Editor<Alias>
alias.setRecordable(getRecordAudioToggleSwitch().isSelected());
alias.setColor(ColorUtil.toInteger(getColorPicker().getValue()));
boolean canMonitor = getMonitorAudioToggleSwitch().isSelected();
Integer priority = getMonitorPriorityComboBox().getSelectionModel().getSelectedItem();
Icon icon = getIconNodeComboBox().getSelectionModel().getSelectedItem();
alias.setIconName(icon != null ? icon.getName() : null);
boolean canMonitor = getMonitorAudioToggleSwitch().isSelected();
Integer priority = getMonitorPriorityComboBox().getSelectionModel().getSelectedItem();
if(canMonitor)
{
if(priority == null)