Added table column adjuster class to help in formatting songs table.
This commit is contained in:
parent
4a44675e8e
commit
0cefc0946a
|
@ -0,0 +1,429 @@
|
||||||
|
package handiebot.utils;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.TableModelEvent;
|
||||||
|
import javax.swing.event.TableModelListener;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
|
import javax.swing.table.TableColumnModel;
|
||||||
|
import javax.swing.table.TableModel;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class to manage the widths of colunmns in a table.
|
||||||
|
*
|
||||||
|
* Various properties control how the width of the column is calculated.
|
||||||
|
* Another property controls whether column width calculation should be dynamic.
|
||||||
|
* Finally, various Actions will be added to the table to allow the user
|
||||||
|
* to customize the functionality.
|
||||||
|
*
|
||||||
|
* This class was designed to be used with tables that use an auto resize mode
|
||||||
|
* of AUTO_RESIZE_OFF. With all other modes you are constrained as the width
|
||||||
|
* of the columns must fit inside the table. So if you increase one column, one
|
||||||
|
* or more of the other columns must decrease. Because of this the resize mode
|
||||||
|
* of RESIZE_ALL_COLUMNS will work the best.
|
||||||
|
*/
|
||||||
|
public class TableColumnAdjuster implements PropertyChangeListener, TableModelListener
|
||||||
|
{
|
||||||
|
private JTable table;
|
||||||
|
private int spacing;
|
||||||
|
private boolean isColumnHeaderIncluded;
|
||||||
|
private boolean isColumnDataIncluded;
|
||||||
|
private boolean isOnlyAdjustLarger;
|
||||||
|
private boolean isDynamicAdjustment;
|
||||||
|
private Map<TableColumn, Integer> columnSizes = new HashMap<TableColumn, Integer>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify the table and use default spacing
|
||||||
|
*/
|
||||||
|
public TableColumnAdjuster(JTable table)
|
||||||
|
{
|
||||||
|
this(table, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify the table and spacing
|
||||||
|
*/
|
||||||
|
public TableColumnAdjuster(JTable table, int spacing)
|
||||||
|
{
|
||||||
|
this.table = table;
|
||||||
|
this.spacing = spacing;
|
||||||
|
setColumnHeaderIncluded( true );
|
||||||
|
setColumnDataIncluded( true );
|
||||||
|
setOnlyAdjustLarger( false );
|
||||||
|
setDynamicAdjustment( false );
|
||||||
|
installActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust the widths of all the columns in the table
|
||||||
|
*/
|
||||||
|
public void adjustColumns()
|
||||||
|
{
|
||||||
|
TableColumnModel tcm = table.getColumnModel();
|
||||||
|
|
||||||
|
for (int i = 0; i < tcm.getColumnCount(); i++)
|
||||||
|
{
|
||||||
|
adjustColumn(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust the width of the specified column in the table
|
||||||
|
*/
|
||||||
|
public void adjustColumn(final int column)
|
||||||
|
{
|
||||||
|
TableColumn tableColumn = table.getColumnModel().getColumn(column);
|
||||||
|
|
||||||
|
if (! tableColumn.getResizable()) return;
|
||||||
|
|
||||||
|
int columnHeaderWidth = getColumnHeaderWidth( column );
|
||||||
|
int columnDataWidth = getColumnDataWidth( column );
|
||||||
|
int preferredWidth = Math.max(columnHeaderWidth, columnDataWidth);
|
||||||
|
|
||||||
|
updateTableColumn(column, preferredWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculated the width based on the column name
|
||||||
|
*/
|
||||||
|
private int getColumnHeaderWidth(int column)
|
||||||
|
{
|
||||||
|
if (! isColumnHeaderIncluded) return 0;
|
||||||
|
|
||||||
|
TableColumn tableColumn = table.getColumnModel().getColumn(column);
|
||||||
|
Object value = tableColumn.getHeaderValue();
|
||||||
|
TableCellRenderer renderer = tableColumn.getHeaderRenderer();
|
||||||
|
|
||||||
|
if (renderer == null)
|
||||||
|
{
|
||||||
|
renderer = table.getTableHeader().getDefaultRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component c = renderer.getTableCellRendererComponent(table, value, false, false, -1, column);
|
||||||
|
return c.getPreferredSize().width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the width based on the widest cell renderer for the
|
||||||
|
* given column.
|
||||||
|
*/
|
||||||
|
private int getColumnDataWidth(int column)
|
||||||
|
{
|
||||||
|
if (! isColumnDataIncluded) return 0;
|
||||||
|
|
||||||
|
int preferredWidth = 0;
|
||||||
|
int maxWidth = table.getColumnModel().getColumn(column).getMaxWidth();
|
||||||
|
|
||||||
|
for (int row = 0; row < table.getRowCount(); row++)
|
||||||
|
{
|
||||||
|
preferredWidth = Math.max(preferredWidth, getCellDataWidth(row, column));
|
||||||
|
|
||||||
|
// We've exceeded the maximum width, no need to check other rows
|
||||||
|
|
||||||
|
if (preferredWidth >= maxWidth)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferredWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the preferred width for the specified cell
|
||||||
|
*/
|
||||||
|
private int getCellDataWidth(int row, int column)
|
||||||
|
{
|
||||||
|
// Inovke the renderer for the cell to calculate the preferred width
|
||||||
|
|
||||||
|
TableCellRenderer cellRenderer = table.getCellRenderer(row, column);
|
||||||
|
Component c = table.prepareRenderer(cellRenderer, row, column);
|
||||||
|
int width = c.getPreferredSize().width + table.getIntercellSpacing().width;
|
||||||
|
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the TableColumn with the newly calculated width
|
||||||
|
*/
|
||||||
|
private void updateTableColumn(int column, int width)
|
||||||
|
{
|
||||||
|
final TableColumn tableColumn = table.getColumnModel().getColumn(column);
|
||||||
|
|
||||||
|
if (! tableColumn.getResizable()) return;
|
||||||
|
|
||||||
|
width += spacing;
|
||||||
|
|
||||||
|
// Don't shrink the column width
|
||||||
|
|
||||||
|
if (isOnlyAdjustLarger)
|
||||||
|
{
|
||||||
|
width = Math.max(width, tableColumn.getPreferredWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
columnSizes.put(tableColumn, new Integer(tableColumn.getWidth()));
|
||||||
|
|
||||||
|
table.getTableHeader().setResizingColumn(tableColumn);
|
||||||
|
tableColumn.setWidth(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the widths of the columns in the table to its previous width
|
||||||
|
*/
|
||||||
|
public void restoreColumns()
|
||||||
|
{
|
||||||
|
TableColumnModel tcm = table.getColumnModel();
|
||||||
|
|
||||||
|
for (int i = 0; i < tcm.getColumnCount(); i++)
|
||||||
|
{
|
||||||
|
restoreColumn(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the width of the specified column to its previous width
|
||||||
|
*/
|
||||||
|
private void restoreColumn(int column)
|
||||||
|
{
|
||||||
|
TableColumn tableColumn = table.getColumnModel().getColumn(column);
|
||||||
|
Integer width = columnSizes.get(tableColumn);
|
||||||
|
|
||||||
|
if (width != null)
|
||||||
|
{
|
||||||
|
table.getTableHeader().setResizingColumn(tableColumn);
|
||||||
|
tableColumn.setWidth( width.intValue() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indicates whether to include the header in the width calculation
|
||||||
|
*/
|
||||||
|
public void setColumnHeaderIncluded(boolean isColumnHeaderIncluded)
|
||||||
|
{
|
||||||
|
this.isColumnHeaderIncluded = isColumnHeaderIncluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indicates whether to include the model data in the width calculation
|
||||||
|
*/
|
||||||
|
public void setColumnDataIncluded(boolean isColumnDataIncluded)
|
||||||
|
{
|
||||||
|
this.isColumnDataIncluded = isColumnDataIncluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indicates whether columns can only be increased in size
|
||||||
|
*/
|
||||||
|
public void setOnlyAdjustLarger(boolean isOnlyAdjustLarger)
|
||||||
|
{
|
||||||
|
this.isOnlyAdjustLarger = isOnlyAdjustLarger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indicate whether changes to the model should cause the width to be
|
||||||
|
* dynamically recalculated.
|
||||||
|
*/
|
||||||
|
public void setDynamicAdjustment(boolean isDynamicAdjustment)
|
||||||
|
{
|
||||||
|
// May need to add or remove the TableModelListener when changed
|
||||||
|
|
||||||
|
if (this.isDynamicAdjustment != isDynamicAdjustment)
|
||||||
|
{
|
||||||
|
if (isDynamicAdjustment)
|
||||||
|
{
|
||||||
|
table.addPropertyChangeListener( this );
|
||||||
|
table.getModel().addTableModelListener( this );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
table.removePropertyChangeListener( this );
|
||||||
|
table.getModel().removeTableModelListener( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isDynamicAdjustment = isDynamicAdjustment;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Implement the PropertyChangeListener
|
||||||
|
//
|
||||||
|
public void propertyChange(PropertyChangeEvent e)
|
||||||
|
{
|
||||||
|
// When the TableModel changes we need to update the listeners
|
||||||
|
// and column widths
|
||||||
|
|
||||||
|
if ("model".equals(e.getPropertyName()))
|
||||||
|
{
|
||||||
|
TableModel model = (TableModel)e.getOldValue();
|
||||||
|
model.removeTableModelListener( this );
|
||||||
|
|
||||||
|
model = (TableModel)e.getNewValue();
|
||||||
|
model.addTableModelListener( this );
|
||||||
|
adjustColumns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Implement the TableModelListener
|
||||||
|
//
|
||||||
|
public void tableChanged(TableModelEvent e)
|
||||||
|
{
|
||||||
|
if (! isColumnDataIncluded) return;
|
||||||
|
|
||||||
|
// Needed when table is sorted.
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(new Runnable()
|
||||||
|
{
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
// A cell has been updated
|
||||||
|
|
||||||
|
int column = table.convertColumnIndexToView(e.getColumn());
|
||||||
|
|
||||||
|
if (e.getType() == TableModelEvent.UPDATE && column != -1)
|
||||||
|
{
|
||||||
|
// Only need to worry about an increase in width for this cell
|
||||||
|
|
||||||
|
if (isOnlyAdjustLarger)
|
||||||
|
{
|
||||||
|
int row = e.getFirstRow();
|
||||||
|
TableColumn tableColumn = table.getColumnModel().getColumn(column);
|
||||||
|
|
||||||
|
if (tableColumn.getResizable())
|
||||||
|
{
|
||||||
|
int width = getCellDataWidth(row, column);
|
||||||
|
updateTableColumn(column, width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Could be an increase of decrease so check all rows
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adjustColumn( column );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The update affected more than one column so adjust all columns
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adjustColumns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Install Actions to give user control of certain functionality.
|
||||||
|
*/
|
||||||
|
private void installActions()
|
||||||
|
{
|
||||||
|
installColumnAction(true, true, "adjustColumn", "control ADD");
|
||||||
|
installColumnAction(false, true, "adjustColumns", "control shift ADD");
|
||||||
|
installColumnAction(true, false, "restoreColumn", "control SUBTRACT");
|
||||||
|
installColumnAction(false, false, "restoreColumns", "control shift SUBTRACT");
|
||||||
|
|
||||||
|
installToggleAction(true, false, "toggleDynamic", "control MULTIPLY");
|
||||||
|
installToggleAction(false, true, "toggleLarger", "control DIVIDE");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the input and action maps with a new ColumnAction
|
||||||
|
*/
|
||||||
|
private void installColumnAction(
|
||||||
|
boolean isSelectedColumn, boolean isAdjust, String key, String keyStroke)
|
||||||
|
{
|
||||||
|
Action action = new ColumnAction(isSelectedColumn, isAdjust);
|
||||||
|
KeyStroke ks = KeyStroke.getKeyStroke( keyStroke );
|
||||||
|
table.getInputMap().put(ks, key);
|
||||||
|
table.getActionMap().put(key, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the input and action maps with new ToggleAction
|
||||||
|
*/
|
||||||
|
private void installToggleAction(
|
||||||
|
boolean isToggleDynamic, boolean isToggleLarger, String key, String keyStroke)
|
||||||
|
{
|
||||||
|
Action action = new ToggleAction(isToggleDynamic, isToggleLarger);
|
||||||
|
KeyStroke ks = KeyStroke.getKeyStroke( keyStroke );
|
||||||
|
table.getInputMap().put(ks, key);
|
||||||
|
table.getActionMap().put(key, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Action to adjust or restore the width of a single column or all columns
|
||||||
|
*/
|
||||||
|
class ColumnAction extends AbstractAction
|
||||||
|
{
|
||||||
|
private boolean isSelectedColumn;
|
||||||
|
private boolean isAdjust;
|
||||||
|
|
||||||
|
public ColumnAction(boolean isSelectedColumn, boolean isAdjust)
|
||||||
|
{
|
||||||
|
this.isSelectedColumn = isSelectedColumn;
|
||||||
|
this.isAdjust = isAdjust;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
// Handle selected column(s) width change actions
|
||||||
|
|
||||||
|
if (isSelectedColumn)
|
||||||
|
{
|
||||||
|
int[] columns = table.getSelectedColumns();
|
||||||
|
|
||||||
|
for (int i = 0; i < columns.length; i++)
|
||||||
|
{
|
||||||
|
if (isAdjust)
|
||||||
|
adjustColumn(columns[i]);
|
||||||
|
else
|
||||||
|
restoreColumn(columns[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isAdjust)
|
||||||
|
adjustColumns();
|
||||||
|
else
|
||||||
|
restoreColumns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Toggle properties of the TableColumnAdjuster so the user can
|
||||||
|
* customize the functionality to their preferences
|
||||||
|
*/
|
||||||
|
class ToggleAction extends AbstractAction
|
||||||
|
{
|
||||||
|
private boolean isToggleDynamic;
|
||||||
|
private boolean isToggleLarger;
|
||||||
|
|
||||||
|
public ToggleAction(boolean isToggleDynamic, boolean isToggleLarger)
|
||||||
|
{
|
||||||
|
this.isToggleDynamic = isToggleDynamic;
|
||||||
|
this.isToggleLarger = isToggleLarger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
if (isToggleDynamic)
|
||||||
|
{
|
||||||
|
setDynamicAdjustment(! isDynamicAdjustment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isToggleLarger)
|
||||||
|
{
|
||||||
|
setOnlyAdjustLarger(! isOnlyAdjustLarger);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,8 +7,6 @@ import handiebot.view.tableModels.SongsTableModel;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.ListSelectionListener;
|
import javax.swing.event.ListSelectionListener;
|
||||||
import javax.swing.table.TableCellRenderer;
|
|
||||||
import javax.swing.table.TableColumnModel;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
|
@ -57,6 +55,7 @@ public class BotWindow extends JFrame {
|
||||||
//Playlist name scroll pane.
|
//Playlist name scroll pane.
|
||||||
playlistTable.setRowSelectionAllowed(true);
|
playlistTable.setRowSelectionAllowed(true);
|
||||||
playlistTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
playlistTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
playlistTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||||
playlistTable.getSelectionModel().addListSelectionListener(new PlaylistSelectionListener(this.songsTableModel, playlistTable, songsTable));
|
playlistTable.getSelectionModel().addListSelectionListener(new PlaylistSelectionListener(this.songsTableModel, playlistTable, songsTable));
|
||||||
JScrollPane playlistNamesScrollPane = new JScrollPane(playlistTable);
|
JScrollPane playlistNamesScrollPane = new JScrollPane(playlistTable);
|
||||||
playlistNamesScrollPane.setPreferredSize(new Dimension(250, 200));
|
playlistNamesScrollPane.setPreferredSize(new Dimension(250, 200));
|
||||||
|
@ -121,25 +120,6 @@ public class BotWindow extends JFrame {
|
||||||
this.playlistDisplayPanel.setVisible(!this.playlistDisplayPanel.isVisible());
|
this.playlistDisplayPanel.setVisible(!this.playlistDisplayPanel.isVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically resizes a table to shrink the index column.
|
|
||||||
* @param table The table to resize.
|
|
||||||
*/
|
|
||||||
public static void autoSizeTable(JTable table){
|
|
||||||
final TableColumnModel columnModel = table.getColumnModel();
|
|
||||||
int freeSpace = 230;
|
|
||||||
for (int col = 0; col < columnModel.getColumnCount(); col++) {
|
|
||||||
int width = 0;
|
|
||||||
for (int row = 0; row < table.getRowCount(); row++) {
|
|
||||||
TableCellRenderer renderer = table.getCellRenderer(row, col);
|
|
||||||
Component component = table.prepareRenderer(renderer, row, col);
|
|
||||||
width = Math.max(component.getPreferredSize().width + 1, width);
|
|
||||||
}
|
|
||||||
columnModel.getColumn(col).setPreferredWidth(width);
|
|
||||||
freeSpace -= width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public JTextPane getOutputArea(){
|
public JTextPane getOutputArea(){
|
||||||
return this.outputArea;
|
return this.outputArea;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package handiebot.view;
|
package handiebot.view;
|
||||||
|
|
||||||
import handiebot.lavaplayer.playlist.Playlist;
|
import handiebot.lavaplayer.playlist.Playlist;
|
||||||
|
import handiebot.utils.TableColumnAdjuster;
|
||||||
import handiebot.view.tableModels.SongsTableModel;
|
import handiebot.view.tableModels.SongsTableModel;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -18,19 +19,21 @@ public class PlaylistSelectionListener implements ListSelectionListener {
|
||||||
|
|
||||||
//The table that shows the playlist names.
|
//The table that shows the playlist names.
|
||||||
private JTable table;
|
private JTable table;
|
||||||
|
//The table for the songs.
|
||||||
private JTable songsTable;
|
private JTable songsTable;
|
||||||
|
private TableColumnAdjuster tca;
|
||||||
|
|
||||||
public PlaylistSelectionListener(SongsTableModel songsModel, JTable table, JTable songsTable){
|
public PlaylistSelectionListener(SongsTableModel songsModel, JTable table, JTable songsTable){
|
||||||
this.songsModel = songsModel;
|
this.songsModel = songsModel;
|
||||||
this.table = table;
|
this.table = table;
|
||||||
this.songsTable = songsTable;
|
this.songsTable = songsTable;
|
||||||
|
this.tca = new TableColumnAdjuster(songsTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void valueChanged(ListSelectionEvent e) {
|
public void valueChanged(ListSelectionEvent e) {
|
||||||
if (e.getValueIsAdjusting()){
|
if (e.getValueIsAdjusting()){
|
||||||
updatePlaylistData();
|
updatePlaylistData();
|
||||||
BotWindow.autoSizeTable(songsTable);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +47,7 @@ public class PlaylistSelectionListener implements ListSelectionListener {
|
||||||
Playlist playlist = new Playlist(selectedValue);
|
Playlist playlist = new Playlist(selectedValue);
|
||||||
playlist.load();
|
playlist.load();
|
||||||
this.songsModel.setPlaylist(playlist);
|
this.songsModel.setPlaylist(playlist);
|
||||||
|
this.tca.adjustColumns();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue