PIbd-21 Potapov N.S. LabWork06 Hard #6
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
11
.idea/PIbd-21_Potapov_N.S._Stormtrooper_Hard.iml
generated
Normal file
11
.idea/PIbd-21_Potapov_N.S._Stormtrooper_Hard.iml
generated
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_20" default="true" project-jdk-name="20 (2)" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/PIbd-21_Potapov_N.S._Stormtrooper_Hard.iml" filepath="$PROJECT_DIR$/.idea/PIbd-21_Potapov_N.S._Stormtrooper_Hard.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -68,7 +68,8 @@ public class DrawingPlane extends JPanel {
|
||||
}
|
||||
|
||||
public void SetEnginesCount(int enginesCount) {
|
||||
_drawingEngines.SetEnumEnginesCount(enginesCount);
|
||||
if (_drawingEngines != null)
|
||||
_drawingEngines.SetEnumEnginesCount(enginesCount);
|
||||
}
|
||||
|
||||
public boolean CanMove(EnumDirectionType direction) {
|
||||
|
87
ProjectStormtrooper/ExtensionDrawingPlane.java
Normal file
87
ProjectStormtrooper/ExtensionDrawingPlane.java
Normal file
@ -0,0 +1,87 @@
|
||||
package ProjectStormtrooper;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ExtensionDrawingPlane {
|
||||
public static DrawingPlane CreateDrawingPlane(String info, String separatorForObject, int width, int height) {
|
||||
String[] strs = info.split(separatorForObject);
|
||||
if (strs.length == 7) {
|
||||
DrawingPlane drawingPlane = new DrawingPlane(
|
||||
Integer.parseInt(strs[0]),
|
||||
Double.parseDouble(strs[1]),
|
||||
new Color(
|
||||
Integer.parseInt(strs[2]),
|
||||
Integer.parseInt(strs[3]),
|
||||
Integer.parseInt(strs[4])
|
||||
),
|
||||
width, height
|
||||
);
|
||||
if (Objects.equals(strs[5], "DrawingEnginesSimple")) {
|
||||
drawingPlane._drawingEngines = new DrawingEnginesSimple();
|
||||
} else if (Objects.equals(strs[5], "DrawingEnginesPyramid")) {
|
||||
drawingPlane._drawingEngines = new DrawingEnginesPyramid();
|
||||
} else if (Objects.equals(strs[5], "DrawingEnginesEllipse")) {
|
||||
drawingPlane._drawingEngines = new DrawingEnginesEllipse();
|
||||
}
|
||||
drawingPlane.SetEnginesCount(Integer.parseInt(strs[6]));
|
||||
return drawingPlane;
|
||||
}
|
||||
if (strs.length == 12) {
|
||||
DrawingPlane drawingPlane = new DrawingStormtrooper(
|
||||
Integer.parseInt(strs[0]),
|
||||
Double.parseDouble(strs[1]),
|
||||
new Color(
|
||||
Integer.parseInt(strs[2]),
|
||||
Integer.parseInt(strs[3]),
|
||||
Integer.parseInt(strs[4])
|
||||
),
|
||||
new Color(
|
||||
Integer.parseInt(strs[7]),
|
||||
Integer.parseInt(strs[8]),
|
||||
Integer.parseInt(strs[9])
|
||||
),
|
||||
Boolean.parseBoolean(strs[10]),
|
||||
Boolean.parseBoolean(strs[11]),
|
||||
width, height
|
||||
);
|
||||
if (Objects.equals(strs[5], "DrawingEnginesSimple")) {
|
||||
drawingPlane._drawingEngines = new DrawingEnginesSimple();
|
||||
} else if (Objects.equals(strs[5], "DrawingEnginesPyramid")) {
|
||||
drawingPlane._drawingEngines = new DrawingEnginesPyramid();
|
||||
} else if (Objects.equals(strs[5], "DrawingEnginesEllipse")) {
|
||||
drawingPlane._drawingEngines = new DrawingEnginesEllipse();
|
||||
}
|
||||
drawingPlane.SetEnginesCount(Integer.parseInt(strs[6]));
|
||||
return drawingPlane;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String GetDataForSave(DrawingPlane drawingPlane, String separatorForObject) {
|
||||
var plane = drawingPlane.EntityPlane;
|
||||
if (plane == null) {
|
||||
return "";
|
||||
}
|
||||
String str = plane.Speed +
|
||||
separatorForObject +
|
||||
plane.Weight +
|
||||
separatorForObject +
|
||||
plane.BodyColor.getRed() + separatorForObject +
|
||||
plane.BodyColor.getGreen() + separatorForObject +
|
||||
plane.BodyColor.getBlue() + separatorForObject +
|
||||
(drawingPlane._drawingEngines.GetEnumEnginesCount() == null ? "null" : drawingPlane._drawingEngines.getClass()) + separatorForObject +
|
||||
(drawingPlane._drawingEngines.GetEnumEnginesCount() == null ? "0" : drawingPlane._drawingEngines.GetEnumEnginesCount().count);
|
||||
if (!(plane instanceof EntityStormtrooper stormtrooper)) {
|
||||
return str;
|
||||
}
|
||||
return str +
|
||||
separatorForObject +
|
||||
stormtrooper.AdditionalColor.getRed() + separatorForObject +
|
||||
stormtrooper.AdditionalColor.getGreen() + separatorForObject +
|
||||
stormtrooper.AdditionalColor.getBlue() + separatorForObject +
|
||||
stormtrooper.Bombs +
|
||||
separatorForObject +
|
||||
stormtrooper.Rockets;
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="ProjectStormtrooper.FormPlaneCollection">
|
||||
<grid id="27dc6" binding="PanelWrapper" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="666" height="454"/>
|
||||
<xy x="20" y="20" width="796" height="600"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@ -10,7 +10,9 @@
|
||||
<grid id="5f693" binding="PictureBoxCollection" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints border-constraint="Center"/>
|
||||
<properties/>
|
||||
<properties>
|
||||
<minimumSize width="24" height="24"/>
|
||||
</properties>
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</grid>
|
||||
|
@ -2,8 +2,11 @@ package ProjectStormtrooper;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
import java.util.Stack;
|
||||
|
||||
@ -34,8 +37,8 @@ public class FormPlaneCollection {
|
||||
}
|
||||
|
||||
public FormPlaneCollection() {
|
||||
PictureBoxCollection.setPreferredSize(new Dimension(600, 500));
|
||||
_storage = new PlanesGenericStorage(600, 500);
|
||||
PictureBoxCollection.setPreferredSize(new Dimension(800, 600));
|
||||
_storage = new PlanesGenericStorage(800, 600);
|
||||
_removedPlanes = new Stack<>();
|
||||
buttonAddPlane.addActionListener(this::buttonAddPlaneClicked);
|
||||
buttonRemovePlane.addActionListener(this::buttonRemovePlaneClicked);
|
||||
@ -47,6 +50,85 @@ public class FormPlaneCollection {
|
||||
buttonShowRemovedPlanes.addActionListener(this::buttonShowRemovedPlanesClicked);
|
||||
}
|
||||
|
||||
public JMenuBar getMenuBar() {
|
||||
JMenuBar menuBar = new JMenuBar();
|
||||
JMenu fileMenu = new JMenu("Файл");
|
||||
JMenuItem openItem = new JMenuItem("Загрузить");
|
||||
openItem.addActionListener(
|
||||
e -> {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("Текстовые файлы (*.txt)", "txt"));
|
||||
fileChooser.setDialogTitle("Выберите файл для загрузки данных");
|
||||
if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
File selectedFile = fileChooser.getSelectedFile();
|
||||
if (_storage.LoadData(selectedFile.getAbsolutePath())) {
|
||||
JOptionPane.showMessageDialog(null, "Загрузка прошла успешно", "Результат", JOptionPane.INFORMATION_MESSAGE);
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Не загрузилось", "Результат", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
ReloadObjects();
|
||||
}
|
||||
);
|
||||
JMenuItem saveItem = new JMenuItem("Сохранить");
|
||||
saveItem.addActionListener(
|
||||
e -> {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setDialogTitle("Выберите файл для сохранения данных");
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("Текстовые файлы (*.txt)", "txt"));
|
||||
if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
File selectedFile = fileChooser.getSelectedFile();
|
||||
if (_storage.SaveData(selectedFile.getAbsolutePath()))
|
||||
JOptionPane.showMessageDialog(null, "Сохранение прошло успешно", "Результат", JOptionPane.INFORMATION_MESSAGE);
|
||||
else
|
||||
JOptionPane.showMessageDialog(null, "Не сохранилось", "Результат", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
);
|
||||
JMenuItem openItemSingle = new JMenuItem("Загрузить коллекцию");
|
||||
openItemSingle.addActionListener(
|
||||
e -> {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("Текстовые файлы (*.txt)", "txt"));
|
||||
fileChooser.setDialogTitle("Выберите файл для загрузки данных");
|
||||
if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
File selectedFile = fileChooser.getSelectedFile();
|
||||
if (_storage.LoadDataSingle(selectedFile.getAbsolutePath())) {
|
||||
JOptionPane.showMessageDialog(null, "Загрузка прошла успешно", "Результат", JOptionPane.INFORMATION_MESSAGE);
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Не загрузилось", "Результат", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
ReloadObjects();
|
||||
}
|
||||
);
|
||||
JMenuItem saveItemSingle = new JMenuItem("Сохранить коллекцию");
|
||||
saveItemSingle.addActionListener(
|
||||
e -> {
|
||||
if (listBoxStorages.getSelectedValue() == null) {
|
||||
JOptionPane.showMessageDialog(null, "Коллекция не выбрана", "Ошибка", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("Текстовые файлы (*.txt)", "txt"));
|
||||
fileChooser.setDialogTitle("Выберите файл для сохранения данных");
|
||||
if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
File selectedFile = fileChooser.getSelectedFile();
|
||||
if (_storage.SaveDataSingle(selectedFile.getAbsolutePath(), (String) listBoxStorages.getSelectedValue()))
|
||||
JOptionPane.showMessageDialog(null, "Сохранение прошло успешно", "Результат", JOptionPane.INFORMATION_MESSAGE);
|
||||
else
|
||||
JOptionPane.showMessageDialog(null, "Не сохранилось", "Результат", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
);
|
||||
fileMenu.add(openItem);
|
||||
fileMenu.add(saveItem);
|
||||
fileMenu.add(openItemSingle);
|
||||
fileMenu.add(saveItemSingle);
|
||||
menuBar.add(fileMenu);
|
||||
return menuBar;
|
||||
}
|
||||
|
||||
private void ReloadObjects() {
|
||||
int index = listBoxStorages.getSelectedIndex();
|
||||
listBoxStorages.setListData(_storage.Keys().toArray());
|
||||
|
@ -10,6 +10,7 @@ public class FramePlaneCollection extends JFrame {
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
_formPlaneCollection = new FormPlaneCollection();
|
||||
setContentPane(_formPlaneCollection.getPanelWrapper());
|
||||
this.setJMenuBar(_formPlaneCollection.getMenuBar());
|
||||
setDefaultLookAndFeelDecorated(false);
|
||||
setLocation(300, 100);
|
||||
pack();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ProjectStormtrooper;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Stack;
|
||||
|
||||
public class PlanesGenericCollection<T extends DrawingPlane, U extends IMoveableObject> {
|
||||
@ -9,6 +10,9 @@ public class PlanesGenericCollection<T extends DrawingPlane, U extends IMoveable
|
||||
private final int _placeSizeWidth = 160;
|
||||
private final int _placeSizeHeight = 120;
|
||||
private SetGeneric<T> _collection;
|
||||
public ArrayList<T> GetPlanes() {
|
||||
return _collection.GetEnumerator();
|
||||
}
|
||||
|
||||
public PlanesGenericCollection(int picWidth, int picHeight) {
|
||||
int horizontalObjectsCount = picWidth / _placeSizeWidth;
|
||||
@ -75,4 +79,8 @@ public class PlanesGenericCollection<T extends DrawingPlane, U extends IMoveable
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
_collection.clear();
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,21 @@
|
||||
package ProjectStormtrooper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PlanesGenericStorage {
|
||||
final HashMap<String, PlanesGenericCollection<DrawingPlane, DrawingObjectPlane>> _planeStorages;
|
||||
private static final String _separatorForKeyValue = "@";
|
||||
private static final String _separatorRecords = ";";
|
||||
private static final String _separatorForObject = ":";
|
||||
private static final String _separatorForObjectSingle = "::";
|
||||
private static final String _keyword = "PlanesStorage";
|
||||
private static final String _keywordSingle = "PlanesStorageSingle";
|
||||
|
||||
public List<String> Keys() {
|
||||
return _planeStorages.keySet().stream().toList();
|
||||
@ -33,10 +43,143 @@ public class PlanesGenericStorage {
|
||||
return _planeStorages.get(ind);
|
||||
return null;
|
||||
}
|
||||
|
||||
public DrawingObjectPlane GetByDoubleParameter(String storageName, int planeIndex) {
|
||||
if (_planeStorages.containsKey(storageName)) {
|
||||
return _planeStorages.get(storageName).GetU(planeIndex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean SaveDataSingle(String filename, String key) {
|
||||
var file = new File(filename);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
StringBuilder data = new StringBuilder();
|
||||
data.append(key).append("\n");
|
||||
for (DrawingPlane elem : _planeStorages.get(key).GetPlanes())
|
||||
data.append(elem != null ? ExtensionDrawingPlane.GetDataForSave(elem, _separatorForObjectSingle) + "\n" : "");
|
||||
if (data.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
|
||||
writer.write(_keywordSingle + System.lineSeparator() + data);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean LoadDataSingle(String filename){
|
||||
if (!new File(filename).exists()) {
|
||||
return false;
|
||||
}
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
|
||||
String s = reader.readLine();
|
||||
if (s == null || s.isEmpty())
|
||||
return false;
|
||||
if (!s.startsWith(_keywordSingle))
|
||||
return false;
|
||||
String key = reader.readLine();
|
||||
if (key == null || key.isEmpty())
|
||||
return false;
|
||||
PlanesGenericCollection<DrawingPlane, DrawingObjectPlane> collection;
|
||||
if (_planeStorages.containsKey(key)){
|
||||
collection = _planeStorages.get(key);
|
||||
collection.clear();
|
||||
}
|
||||
else
|
||||
collection = new PlanesGenericCollection<>(_pictureWidth, _pictureHeight);
|
||||
List<String> plainsStrings = new ArrayList<>();
|
||||
s = reader.readLine();
|
||||
while (s != null && !s.isEmpty()){
|
||||
plainsStrings.add(s);
|
||||
s = reader.readLine();
|
||||
}
|
||||
Collections.reverse(plainsStrings);
|
||||
for (String elem : plainsStrings) {
|
||||
DrawingPlane plane = ExtensionDrawingPlane.CreateDrawingPlane(
|
||||
elem,
|
||||
_separatorForObjectSingle,
|
||||
_pictureWidth,
|
||||
_pictureHeight
|
||||
);
|
||||
if (plane == null || collection.Add(plane) == -1)
|
||||
return false;
|
||||
plane.SetDrawingBounds(1000, 1000);
|
||||
}
|
||||
_planeStorages.put(key, collection);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean SaveData(String filename) {
|
||||
var file = new File(filename);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
StringBuilder data = new StringBuilder();
|
||||
for (Map.Entry<String, PlanesGenericCollection<DrawingPlane, DrawingObjectPlane>> record : _planeStorages.entrySet()) {
|
||||
StringBuilder records = new StringBuilder();
|
||||
for (DrawingPlane elem : record.getValue().GetPlanes()) {
|
||||
records.append(elem != null ? ExtensionDrawingPlane.GetDataForSave(elem, _separatorForObject) + _separatorRecords : "");
|
||||
}
|
||||
data.append(record.getKey()).append(_separatorForKeyValue).append(records).append("\n");
|
||||
}
|
||||
if (data.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
|
||||
writer.write(_keyword + System.lineSeparator() + data);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean LoadData(String filename) {
|
||||
var file = new File(filename);
|
||||
if (!file.exists()) {
|
||||
return false;
|
||||
}
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
|
||||
String s = reader.readLine();
|
||||
if (s == null || s.isEmpty())
|
||||
return false;
|
||||
|
||||
if (!s.startsWith(_keyword)) {
|
||||
return false;
|
||||
}
|
||||
_planeStorages.clear();
|
||||
s = reader.readLine();
|
||||
while (s != null && !s.isEmpty()) {
|
||||
String[] record = s.split(_separatorForKeyValue);
|
||||
s = reader.readLine();
|
||||
if (record.length != 2) {
|
||||
continue;
|
||||
}
|
||||
PlanesGenericCollection<DrawingPlane, DrawingObjectPlane> collection = new PlanesGenericCollection<>(_pictureWidth, _pictureHeight);
|
||||
String[] set = record[1].split(_separatorRecords);
|
||||
List<String> reversedSet = Arrays.asList(set);
|
||||
Collections.reverse(reversedSet);
|
||||
for (String elem : reversedSet) {
|
||||
DrawingPlane plane = ExtensionDrawingPlane.CreateDrawingPlane(
|
||||
elem,
|
||||
_separatorForObject,
|
||||
_pictureWidth, _pictureHeight
|
||||
);
|
||||
if (plane == null || collection.Add(plane) == -1)
|
||||
return false;
|
||||
plane.SetDrawingBounds(1000, 1000);
|
||||
}
|
||||
_planeStorages.put(record[0], collection);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -63,4 +63,8 @@ public class SetGeneric<T extends DrawingPlane> {
|
||||
public ArrayList<T> GetEnumerator() {
|
||||
return _places;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
_places.clear();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user