mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 22:10:54 +03:00
Tree and List: fixed painting of rounded drop backgrounds (issue #1023)
This commit is contained in:
@@ -1,6 +1,13 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 3.7-SNAPSHOT
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Tree and List: Fixed painting of rounded drop backgrounds. (issue #1023)
|
||||||
|
|
||||||
|
|
||||||
## 3.6.1
|
## 3.6.1
|
||||||
|
|
||||||
- Extras: Support JSVG 2.0.0. Minimum JSVG version is now 1.6.0. (issue #997)
|
- Extras: Support JSVG 2.0.0. Minimum JSVG version is now 1.6.0. (issue #997)
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ public class FlatListUI
|
|||||||
ListModel dataModel, ListSelectionModel selModel, int leadIndex )
|
ListModel dataModel, ListSelectionModel selModel, int leadIndex )
|
||||||
{
|
{
|
||||||
boolean isSelected = selModel.isSelectedIndex( row );
|
boolean isSelected = selModel.isSelectedIndex( row );
|
||||||
|
boolean isDropRow = isDropRow( row );
|
||||||
|
|
||||||
// paint alternating rows
|
// paint alternating rows
|
||||||
if( alternateRowColor != null && row % 2 != 0 &&
|
if( alternateRowColor != null && row % 2 != 0 &&
|
||||||
@@ -335,7 +336,7 @@ public class FlatListUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rounded selection or selection insets
|
// rounded selection or selection insets
|
||||||
if( isSelected &&
|
if( (isSelected || isDropRow) &&
|
||||||
!isFileList && // rounded selection is not supported for file list
|
!isFileList && // rounded selection is not supported for file list
|
||||||
(rendererComponent instanceof DefaultListCellRenderer ||
|
(rendererComponent instanceof DefaultListCellRenderer ||
|
||||||
rendererComponent instanceof BasicComboBoxRenderer) &&
|
rendererComponent instanceof BasicComboBoxRenderer) &&
|
||||||
@@ -376,7 +377,22 @@ public class FlatListUI
|
|||||||
this.getColor() == rendererComponent.getBackground() )
|
this.getColor() == rendererComponent.getBackground() )
|
||||||
{
|
{
|
||||||
inPaintSelection = true;
|
inPaintSelection = true;
|
||||||
paintCellSelection( this, row, x, y, width, height );
|
if( isDropRow ) {
|
||||||
|
// for rounded drop background, it is necessary to first
|
||||||
|
// paint selection background because may be not rounded on some corners
|
||||||
|
if( isSelected ) {
|
||||||
|
Color oldColor = getColor();
|
||||||
|
setColor( list.getSelectionBackground() );
|
||||||
|
paintCellSelection( this, row, x, y, width, height );
|
||||||
|
setColor( oldColor );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint drop background
|
||||||
|
float arc = UIScale.scale( selectionArc / 2f );
|
||||||
|
FlatUIUtils.paintSelection( this, x, y, width, height,
|
||||||
|
UIScale.scale( selectionInsets ), arc, arc, arc, arc, 0 );
|
||||||
|
} else
|
||||||
|
paintCellSelection( this, row, x, y, width, height );
|
||||||
inPaintSelection = false;
|
inPaintSelection = false;
|
||||||
} else
|
} else
|
||||||
super.fillRect( x, y, width, height );
|
super.fillRect( x, y, width, height );
|
||||||
@@ -475,4 +491,15 @@ public class FlatListUI
|
|||||||
FlatListUI ui = (FlatListUI) list.getUI();
|
FlatListUI ui = (FlatListUI) list.getUI();
|
||||||
ui.paintCellSelection( g, row, x, y, width, height );
|
ui.paintCellSelection( g, row, x, y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether dropping on a row.
|
||||||
|
* See DefaultListCellRenderer.getListCellRendererComponent().
|
||||||
|
*/
|
||||||
|
private boolean isDropRow( int row ) {
|
||||||
|
JList.DropLocation dropLocation = list.getDropLocation();
|
||||||
|
return dropLocation != null &&
|
||||||
|
!dropLocation.isInsert() &&
|
||||||
|
dropLocation.getIndex() == row;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,7 +577,6 @@ public class FlatTreeUI
|
|||||||
boolean isEditing = (editingComponent != null && editingRow == row);
|
boolean isEditing = (editingComponent != null && editingRow == row);
|
||||||
boolean isSelected = tree.isRowSelected( row );
|
boolean isSelected = tree.isRowSelected( row );
|
||||||
boolean isDropRow = isDropRow( row );
|
boolean isDropRow = isDropRow( row );
|
||||||
boolean needsSelectionPainting = (isSelected || isDropRow) && isPaintSelection();
|
|
||||||
|
|
||||||
// paint alternating rows
|
// paint alternating rows
|
||||||
if( alternateRowColor != null && row % 2 != 0 ) {
|
if( alternateRowColor != null && row % 2 != 0 ) {
|
||||||
@@ -608,7 +607,7 @@ public class FlatTreeUI
|
|||||||
if( isSelected && isWideSelection() ) {
|
if( isSelected && isWideSelection() ) {
|
||||||
Color oldColor = g.getColor();
|
Color oldColor = g.getColor();
|
||||||
g.setColor( selectionInactiveBackground );
|
g.setColor( selectionInactiveBackground );
|
||||||
paintWideSelection( g, bounds, row );
|
paintWideSelection( g, bounds, row, false );
|
||||||
g.setColor( oldColor );
|
g.setColor( oldColor );
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -628,7 +627,7 @@ public class FlatTreeUI
|
|||||||
|
|
||||||
// renderer background/foreground
|
// renderer background/foreground
|
||||||
Color oldBackgroundSelectionColor = null;
|
Color oldBackgroundSelectionColor = null;
|
||||||
if( isSelected && !hasFocus && !isDropRow ) {
|
if( isSelected && !hasFocus ) {
|
||||||
// apply inactive selection background/foreground if tree is not focused
|
// apply inactive selection background/foreground if tree is not focused
|
||||||
oldBackgroundSelectionColor = setRendererBackgroundSelectionColor( rendererComponent, selectionInactiveBackground );
|
oldBackgroundSelectionColor = setRendererBackgroundSelectionColor( rendererComponent, selectionInactiveBackground );
|
||||||
setRendererForeground( rendererComponent, selectionInactiveForeground );
|
setRendererForeground( rendererComponent, selectionInactiveForeground );
|
||||||
@@ -655,26 +654,12 @@ public class FlatTreeUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// paint selection background
|
// paint selection background
|
||||||
if( needsSelectionPainting ) {
|
if( isSelected && isPaintSelection() ) {
|
||||||
// set selection color
|
Color selectionColor = rendererComponent instanceof DefaultTreeCellRenderer
|
||||||
Color oldColor = g.getColor();
|
? ((DefaultTreeCellRenderer)rendererComponent).getBackgroundSelectionColor()
|
||||||
g.setColor( isDropRow
|
: (hasFocus ? selectionBackground : selectionInactiveBackground);
|
||||||
? UIManager.getColor( "Tree.dropCellBackground" )
|
|
||||||
: (rendererComponent instanceof DefaultTreeCellRenderer
|
|
||||||
? ((DefaultTreeCellRenderer)rendererComponent).getBackgroundSelectionColor()
|
|
||||||
: (hasFocus ? selectionBackground : selectionInactiveBackground)) );
|
|
||||||
|
|
||||||
if( isWideSelection() ) {
|
paintRowSelection( g, selectionColor, rendererComponent, bounds, row, hasFocus, false );
|
||||||
// wide selection
|
|
||||||
paintWideSelection( g, bounds, row );
|
|
||||||
} else {
|
|
||||||
// non-wide selection
|
|
||||||
paintCellBackground( g, rendererComponent, bounds, row, true );
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is actually not necessary because renderer should always set color
|
|
||||||
// before painting, but doing anyway to avoid any side effect (in bad renderers)
|
|
||||||
g.setColor( oldColor );
|
|
||||||
} else {
|
} else {
|
||||||
// paint cell background if DefaultTreeCellRenderer.getBackgroundNonSelectionColor() is set
|
// paint cell background if DefaultTreeCellRenderer.getBackgroundNonSelectionColor() is set
|
||||||
if( rendererComponent instanceof DefaultTreeCellRenderer ) {
|
if( rendererComponent instanceof DefaultTreeCellRenderer ) {
|
||||||
@@ -683,12 +668,19 @@ public class FlatTreeUI
|
|||||||
if( bg != null && !bg.equals( defaultCellNonSelectionBackground ) ) {
|
if( bg != null && !bg.equals( defaultCellNonSelectionBackground ) ) {
|
||||||
Color oldColor = g.getColor();
|
Color oldColor = g.getColor();
|
||||||
g.setColor( bg );
|
g.setColor( bg );
|
||||||
paintCellBackground( g, rendererComponent, bounds, row, false );
|
paintCellBackground( g, rendererComponent, bounds, row, false, false );
|
||||||
g.setColor( oldColor );
|
g.setColor( oldColor );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paint drop background
|
||||||
|
// (this needs to be an extra step for rounded selection)
|
||||||
|
if( isDropRow && isPaintSelection() ) {
|
||||||
|
paintRowSelection( g, UIManager.getColor( "Tree.dropCellBackground" ),
|
||||||
|
rendererComponent, bounds, row, hasFocus, true );
|
||||||
|
}
|
||||||
|
|
||||||
// paint renderer
|
// paint renderer
|
||||||
rendererPane.paintComponent( g, rendererComponent, tree, bounds.x, bounds.y, bounds.width, bounds.height, true );
|
rendererPane.paintComponent( g, rendererComponent, tree, bounds.x, bounds.y, bounds.width, bounds.height, true );
|
||||||
|
|
||||||
@@ -699,6 +691,26 @@ public class FlatTreeUI
|
|||||||
((DefaultTreeCellRenderer)rendererComponent).setBorderSelectionColor( oldBorderSelectionColor );
|
((DefaultTreeCellRenderer)rendererComponent).setBorderSelectionColor( oldBorderSelectionColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void paintRowSelection( Graphics g, Color color, Component rendererComponent,
|
||||||
|
Rectangle bounds, int row, boolean hasFocus, boolean paintDropSelection )
|
||||||
|
{
|
||||||
|
// set selection color
|
||||||
|
Color oldColor = g.getColor();
|
||||||
|
g.setColor( color );
|
||||||
|
|
||||||
|
if( isWideSelection() ) {
|
||||||
|
// wide selection
|
||||||
|
paintWideSelection( g, bounds, row, paintDropSelection );
|
||||||
|
} else {
|
||||||
|
// non-wide selection
|
||||||
|
paintCellBackground( g, rendererComponent, bounds, row, true, paintDropSelection );
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is actually not necessary because renderer should always set color
|
||||||
|
// before painting, but doing anyway to avoid any side effect (in bad renderers)
|
||||||
|
g.setColor( oldColor );
|
||||||
|
}
|
||||||
|
|
||||||
private Color setRendererBackgroundSelectionColor( Component rendererComponent, Color color ) {
|
private Color setRendererBackgroundSelectionColor( Component rendererComponent, Color color ) {
|
||||||
Color oldColor = null;
|
Color oldColor = null;
|
||||||
|
|
||||||
@@ -735,11 +747,11 @@ public class FlatTreeUI
|
|||||||
return oldColor;
|
return oldColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void paintWideSelection( Graphics g, Rectangle bounds, int row ) {
|
private void paintWideSelection( Graphics g, Rectangle bounds, int row, boolean paintDropSelection ) {
|
||||||
float arcTop, arcBottom;
|
float arcTop, arcBottom;
|
||||||
arcTop = arcBottom = UIScale.scale( selectionArc / 2f );
|
arcTop = arcBottom = UIScale.scale( selectionArc / 2f );
|
||||||
|
|
||||||
if( useUnitedRoundedSelection() ) {
|
if( useUnitedRoundedSelection() && !paintDropSelection ) {
|
||||||
if( row > 0 && tree.isRowSelected( row - 1 ) )
|
if( row > 0 && tree.isRowSelected( row - 1 ) )
|
||||||
arcTop = 0;
|
arcTop = 0;
|
||||||
if( row < tree.getRowCount() - 1 && tree.isRowSelected( row + 1 ) )
|
if( row < tree.getRowCount() - 1 && tree.isRowSelected( row + 1 ) )
|
||||||
@@ -751,7 +763,7 @@ public class FlatTreeUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds,
|
private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds,
|
||||||
int row, boolean paintSelection )
|
int row, boolean paintSelection, boolean paintDropSelection )
|
||||||
{
|
{
|
||||||
int xOffset = 0;
|
int xOffset = 0;
|
||||||
int imageOffset = 0;
|
int imageOffset = 0;
|
||||||
@@ -769,7 +781,7 @@ public class FlatTreeUI
|
|||||||
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
||||||
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
||||||
|
|
||||||
if( useUnitedRoundedSelection() ) {
|
if( useUnitedRoundedSelection() && !paintDropSelection ) {
|
||||||
if( row > 0 && tree.isRowSelected( row - 1 ) ) {
|
if( row > 0 && tree.isRowSelected( row - 1 ) ) {
|
||||||
Rectangle r = getPathBounds( tree, tree.getPathForRow( row - 1 ) );
|
Rectangle r = getPathBounds( tree, tree.getPathForRow( row - 1 ) );
|
||||||
arcTopLeft = Math.min( arcTopLeft, r.x - bounds.x );
|
arcTopLeft = Math.min( arcTopLeft, r.x - bounds.x );
|
||||||
|
|||||||
@@ -162,6 +162,11 @@ public class FlatComponents2Test
|
|||||||
|
|
||||||
for( JTree tree : allTrees )
|
for( JTree tree : allTrees )
|
||||||
expandTree( tree );
|
expandTree( tree );
|
||||||
|
|
||||||
|
// drop mode
|
||||||
|
dropModeComboBox.init( DropMode.class, false );
|
||||||
|
dropModeComboBox.setSelectedItem( DropMode.ON_OR_INSERT );
|
||||||
|
dropModeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTableEditors( JTable table ) {
|
private void initTableEditors( JTable table ) {
|
||||||
@@ -311,13 +316,6 @@ public class FlatComponents2Test
|
|||||||
xTable1.setDragEnabled( dnd );
|
xTable1.setDragEnabled( dnd );
|
||||||
xTreeTable1.setDragEnabled( dnd );
|
xTreeTable1.setDragEnabled( dnd );
|
||||||
|
|
||||||
DropMode dropMode = dnd ? DropMode.ON_OR_INSERT : DropMode.USE_SELECTION;
|
|
||||||
list1.setDropMode( dropMode );
|
|
||||||
tree1.setDropMode( dropMode );
|
|
||||||
table1.setDropMode( dropMode );
|
|
||||||
xTable1.setDropMode( dropMode );
|
|
||||||
xTreeTable1.setDropMode( dropMode );
|
|
||||||
|
|
||||||
String key = "FlatLaf.oldTransferHandler";
|
String key = "FlatLaf.oldTransferHandler";
|
||||||
if( dnd ) {
|
if( dnd ) {
|
||||||
list1.putClientProperty( key, list1.getTransferHandler() );
|
list1.putClientProperty( key, list1.getTransferHandler() );
|
||||||
@@ -341,6 +339,32 @@ public class FlatComponents2Test
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dropModeChanged() {
|
||||||
|
DropMode dropMode = dropModeComboBox.getSelectedValue();
|
||||||
|
DropMode dropMode2;
|
||||||
|
switch( dropMode ) {
|
||||||
|
case INSERT_ROWS:
|
||||||
|
case INSERT_COLS:
|
||||||
|
dropMode2 = DropMode.INSERT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ON_OR_INSERT_ROWS:
|
||||||
|
case ON_OR_INSERT_COLS:
|
||||||
|
dropMode2 = DropMode.ON_OR_INSERT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dropMode2 = dropMode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
list1.setDropMode( dropMode2 );
|
||||||
|
tree1.setDropMode( dropMode2 );
|
||||||
|
table1.setDropMode( dropMode );
|
||||||
|
xTable1.setDropMode( dropMode );
|
||||||
|
xTreeTable1.setDropMode( dropMode );
|
||||||
|
}
|
||||||
|
|
||||||
private void tableHeaderButtonChanged() {
|
private void tableHeaderButtonChanged() {
|
||||||
tableHeaderButtonChanged( table1ScrollPane );
|
tableHeaderButtonChanged( table1ScrollPane );
|
||||||
tableHeaderButtonChanged( xTable1ScrollPane );
|
tableHeaderButtonChanged( xTable1ScrollPane );
|
||||||
@@ -618,6 +642,7 @@ public class FlatComponents2Test
|
|||||||
super.updateUI();
|
super.updateUI();
|
||||||
|
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
|
dropModeChanged();
|
||||||
showHorizontalLinesChanged();
|
showHorizontalLinesChanged();
|
||||||
showVerticalLinesChanged();
|
showVerticalLinesChanged();
|
||||||
intercellSpacingChanged();
|
intercellSpacingChanged();
|
||||||
@@ -673,6 +698,7 @@ public class FlatComponents2Test
|
|||||||
leftSelectionInsetsCheckBox = new JCheckBox();
|
leftSelectionInsetsCheckBox = new JCheckBox();
|
||||||
rightSelectionInsetsCheckBox = new JCheckBox();
|
rightSelectionInsetsCheckBox = new JCheckBox();
|
||||||
dndCheckBox = new JCheckBox();
|
dndCheckBox = new JCheckBox();
|
||||||
|
dropModeComboBox = new FlatTestEnumComboBox<>();
|
||||||
listOptionsPanel = new JPanel();
|
listOptionsPanel = new JPanel();
|
||||||
JLabel listRendererLabel = new JLabel();
|
JLabel listRendererLabel = new JLabel();
|
||||||
listRendererComboBox = new JComboBox<>();
|
listRendererComboBox = new JComboBox<>();
|
||||||
@@ -963,6 +989,7 @@ public class FlatComponents2Test
|
|||||||
"[]0" +
|
"[]0" +
|
||||||
"[]0" +
|
"[]0" +
|
||||||
"[]rel" +
|
"[]rel" +
|
||||||
|
"[]" +
|
||||||
"[]"));
|
"[]"));
|
||||||
|
|
||||||
//---- roundedSelectionCheckBox ----
|
//---- roundedSelectionCheckBox ----
|
||||||
@@ -1004,6 +1031,10 @@ public class FlatComponents2Test
|
|||||||
dndCheckBox.setMnemonic('D');
|
dndCheckBox.setMnemonic('D');
|
||||||
dndCheckBox.addActionListener(e -> dndChanged());
|
dndCheckBox.addActionListener(e -> dndChanged());
|
||||||
generalOptionsPanel.add(dndCheckBox, "cell 0 4");
|
generalOptionsPanel.add(dndCheckBox, "cell 0 4");
|
||||||
|
|
||||||
|
//---- dropModeComboBox ----
|
||||||
|
dropModeComboBox.addActionListener(e -> dropModeChanged());
|
||||||
|
generalOptionsPanel.add(dropModeComboBox, "cell 0 5");
|
||||||
}
|
}
|
||||||
add(generalOptionsPanel, "cell 0 4 4 1");
|
add(generalOptionsPanel, "cell 0 4 4 1");
|
||||||
|
|
||||||
@@ -1272,6 +1303,7 @@ public class FlatComponents2Test
|
|||||||
private JCheckBox leftSelectionInsetsCheckBox;
|
private JCheckBox leftSelectionInsetsCheckBox;
|
||||||
private JCheckBox rightSelectionInsetsCheckBox;
|
private JCheckBox rightSelectionInsetsCheckBox;
|
||||||
private JCheckBox dndCheckBox;
|
private JCheckBox dndCheckBox;
|
||||||
|
private FlatTestEnumComboBox<DropMode> dropModeComboBox;
|
||||||
private JPanel listOptionsPanel;
|
private JPanel listOptionsPanel;
|
||||||
private JComboBox<String> listRendererComboBox;
|
private JComboBox<String> listRendererComboBox;
|
||||||
private JComboBox<String> listLayoutOrientationField;
|
private JComboBox<String> listLayoutOrientationField;
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ new FormModel {
|
|||||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||||
"$layoutConstraints": "insets 8,hidemode 3"
|
"$layoutConstraints": "insets 8,hidemode 3"
|
||||||
"$columnConstraints": "[left]"
|
"$columnConstraints": "[left]"
|
||||||
"$rowConstraints": "[][]0[]0[]rel[]"
|
"$rowConstraints": "[][]0[]0[]rel[][]"
|
||||||
} ) {
|
} ) {
|
||||||
name: "generalOptionsPanel"
|
name: "generalOptionsPanel"
|
||||||
"border": new javax.swing.border.TitledBorder( "General Control" )
|
"border": new javax.swing.border.TitledBorder( "General Control" )
|
||||||
@@ -379,6 +379,16 @@ new FormModel {
|
|||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 4"
|
"value": "cell 0 4"
|
||||||
} )
|
} )
|
||||||
|
add( new FormComponent( "com.formdev.flatlaf.testing.FlatTestEnumComboBox" ) {
|
||||||
|
name: "dropModeComboBox"
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.variableLocal": false
|
||||||
|
"JavaCodeGenerator.typeParameters": "DropMode"
|
||||||
|
}
|
||||||
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dropModeChanged", false ) )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 0 5"
|
||||||
|
} )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 4 4 1"
|
"value": "cell 0 4 4 1"
|
||||||
} )
|
} )
|
||||||
|
|||||||
Reference in New Issue
Block a user