HaikuDepot: Remove Custom List
Further removal of the use of custom list class; this time with the generic undo functionality. Relates To #15534 Change-Id: I804a31abc07f42f4f1695562b6f948feb465db0c Reviewed-on: https://review.haiku-os.org/c/haiku/+/3739 Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
@@ -87,7 +87,6 @@ local textDocumentSources =
|
|||||||
CompoundEdit.cpp
|
CompoundEdit.cpp
|
||||||
EditContext.cpp
|
EditContext.cpp
|
||||||
EditManager.cpp
|
EditManager.cpp
|
||||||
EditStack.cpp
|
|
||||||
UndoableEdit.cpp
|
UndoableEdit.cpp
|
||||||
|
|
||||||
# textview
|
# textview
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2112, Stephan Aßmus <[email protected]>
|
* Copyright 2006-2112, Stephan Aßmus <[email protected]>
|
||||||
|
* Copyright 2021, Andrew Lindesay <[email protected]>
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -33,10 +34,10 @@ CompoundEdit::Perform(EditContext& context)
|
|||||||
{
|
{
|
||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
|
|
||||||
int32 count = fEdits.CountItems();
|
int32 count = static_cast<int32>(fEdits.size());
|
||||||
int32 i = 0;
|
int32 i = 0;
|
||||||
for (; i < count; i++) {
|
for (; i < count; i++) {
|
||||||
status = fEdits.ItemAtFast(i)->Perform(context);
|
status = fEdits[i]->Perform(context);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -45,7 +46,7 @@ CompoundEdit::Perform(EditContext& context)
|
|||||||
// roll back
|
// roll back
|
||||||
i--;
|
i--;
|
||||||
for (; i >= 0; i--) {
|
for (; i >= 0; i--) {
|
||||||
fEdits.ItemAtFast(i)->Undo(context);
|
fEdits[i]->Undo(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,10 +59,10 @@ CompoundEdit::Undo(EditContext& context)
|
|||||||
{
|
{
|
||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
|
|
||||||
int32 count = fEdits.CountItems();
|
int32 count = static_cast<int32>(fEdits.size());
|
||||||
int32 i = count - 1;
|
int32 i = count - 1;
|
||||||
for (; i >= 0; i--) {
|
for (; i >= 0; i--) {
|
||||||
status = fEdits.ItemAtFast(i)->Undo(context);
|
status = fEdits[i]->Undo(context);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -70,7 +71,7 @@ CompoundEdit::Undo(EditContext& context)
|
|||||||
// roll back
|
// roll back
|
||||||
i++;
|
i++;
|
||||||
for (; i < count; i++) {
|
for (; i < count; i++) {
|
||||||
fEdits.ItemAtFast(i)->Redo(context);
|
fEdits[i]->Redo(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,10 +84,10 @@ CompoundEdit::Redo(EditContext& context)
|
|||||||
{
|
{
|
||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
|
|
||||||
int32 count = fEdits.CountItems();
|
int32 count = static_cast<int32>(fEdits.size());
|
||||||
int32 i = 0;
|
int32 i = 0;
|
||||||
for (; i < count; i++) {
|
for (; i < count; i++) {
|
||||||
status = fEdits.ItemAtFast(i)->Redo(context);
|
status = fEdits[i]->Redo(context);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -95,7 +96,7 @@ CompoundEdit::Redo(EditContext& context)
|
|||||||
// roll back
|
// roll back
|
||||||
i--;
|
i--;
|
||||||
for (; i >= 0; i--) {
|
for (; i >= 0; i--) {
|
||||||
fEdits.ItemAtFast(i)->Undo(context);
|
fEdits[i]->Undo(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +111,8 @@ CompoundEdit::GetName(BString& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
void
|
||||||
CompoundEdit::AppendEdit(const UndoableEditRef& edit)
|
CompoundEdit::AppendEdit(const UndoableEditRef& edit)
|
||||||
{
|
{
|
||||||
return fEdits.Add(edit);
|
fEdits.push_back(edit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2112, Stephan Aßmus <[email protected]>
|
* Copyright 2006-2112, Stephan Aßmus <[email protected]>
|
||||||
|
* Copyright 2021, Andrew Lindesay <[email protected]>
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef COMPOUND_EDIT_H
|
#ifndef COMPOUND_EDIT_H
|
||||||
#define COMPOUND_EDIT_H
|
#define COMPOUND_EDIT_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
#include "List.h"
|
|
||||||
#include "UndoableEdit.h"
|
#include "UndoableEdit.h"
|
||||||
|
|
||||||
class CompoundEdit : public UndoableEdit {
|
class CompoundEdit : public UndoableEdit {
|
||||||
@@ -24,12 +26,11 @@ public:
|
|||||||
|
|
||||||
virtual void GetName(BString& name);
|
virtual void GetName(BString& name);
|
||||||
|
|
||||||
bool AppendEdit(const UndoableEditRef& edit);
|
void AppendEdit(const UndoableEditRef& edit);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef List<UndoableEditRef, false, 2> EditList;
|
std::vector<UndoableEditRef>
|
||||||
|
fEdits;
|
||||||
EditList fEdits;
|
|
||||||
BString fName;
|
BString fName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2012, Stephan Aßmus <[email protected]>
|
* Copyright 2006-2012, Stephan Aßmus <[email protected]>
|
||||||
|
* Copyright 2021, Andrew Lindesay <[email protected]>
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EditManager.h"
|
#include "EditManager.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -64,14 +67,14 @@ status_t
|
|||||||
EditManager::Undo(EditContext& context)
|
EditManager::Undo(EditContext& context)
|
||||||
{
|
{
|
||||||
status_t status = B_ERROR;
|
status_t status = B_ERROR;
|
||||||
if (!fUndoHistory.IsEmpty()) {
|
if (!fUndoHistory.empty()) {
|
||||||
UndoableEditRef edit(fUndoHistory.Top());
|
UndoableEditRef edit(fUndoHistory.top());
|
||||||
fUndoHistory.Pop();
|
fUndoHistory.pop();
|
||||||
status = edit->Undo(context);
|
status = edit->Undo(context);
|
||||||
if (status == B_OK)
|
if (status == B_OK)
|
||||||
fRedoHistory.Push(edit);
|
fRedoHistory.push(edit);
|
||||||
else
|
else
|
||||||
fUndoHistory.Push(edit);
|
fUndoHistory.push(edit);
|
||||||
}
|
}
|
||||||
|
|
||||||
_NotifyListeners();
|
_NotifyListeners();
|
||||||
@@ -84,14 +87,14 @@ status_t
|
|||||||
EditManager::Redo(EditContext& context)
|
EditManager::Redo(EditContext& context)
|
||||||
{
|
{
|
||||||
status_t status = B_ERROR;
|
status_t status = B_ERROR;
|
||||||
if (!fRedoHistory.IsEmpty()) {
|
if (!fRedoHistory.empty()) {
|
||||||
UndoableEditRef edit(fRedoHistory.Top());
|
UndoableEditRef edit(fRedoHistory.top());
|
||||||
fRedoHistory.Pop();
|
fRedoHistory.pop();
|
||||||
status = edit->Redo(context);
|
status = edit->Redo(context);
|
||||||
if (status == B_OK)
|
if (status == B_OK)
|
||||||
fUndoHistory.Push(edit);
|
fUndoHistory.push(edit);
|
||||||
else
|
else
|
||||||
fRedoHistory.Push(edit);
|
fRedoHistory.push(edit);
|
||||||
}
|
}
|
||||||
|
|
||||||
_NotifyListeners();
|
_NotifyListeners();
|
||||||
@@ -103,9 +106,9 @@ EditManager::Redo(EditContext& context)
|
|||||||
bool
|
bool
|
||||||
EditManager::GetUndoName(BString& name)
|
EditManager::GetUndoName(BString& name)
|
||||||
{
|
{
|
||||||
if (!fUndoHistory.IsEmpty()) {
|
if (!fUndoHistory.empty()) {
|
||||||
name << " ";
|
name << " ";
|
||||||
fUndoHistory.Top()->GetName(name);
|
fUndoHistory.top()->GetName(name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -115,9 +118,9 @@ EditManager::GetUndoName(BString& name)
|
|||||||
bool
|
bool
|
||||||
EditManager::GetRedoName(BString& name)
|
EditManager::GetRedoName(BString& name)
|
||||||
{
|
{
|
||||||
if (!fRedoHistory.IsEmpty()) {
|
if (!fRedoHistory.empty()) {
|
||||||
name << " ";
|
name << " ";
|
||||||
fRedoHistory.Top()->GetName(name);
|
fRedoHistory.top()->GetName(name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -127,10 +130,10 @@ EditManager::GetRedoName(BString& name)
|
|||||||
void
|
void
|
||||||
EditManager::Clear()
|
EditManager::Clear()
|
||||||
{
|
{
|
||||||
while (!fUndoHistory.IsEmpty())
|
while (!fUndoHistory.empty())
|
||||||
fUndoHistory.Pop();
|
fUndoHistory.pop();
|
||||||
while (!fRedoHistory.IsEmpty())
|
while (!fRedoHistory.empty())
|
||||||
fRedoHistory.Pop();
|
fRedoHistory.pop();
|
||||||
|
|
||||||
_NotifyListeners();
|
_NotifyListeners();
|
||||||
}
|
}
|
||||||
@@ -139,8 +142,8 @@ EditManager::Clear()
|
|||||||
void
|
void
|
||||||
EditManager::Save()
|
EditManager::Save()
|
||||||
{
|
{
|
||||||
if (!fUndoHistory.IsEmpty())
|
if (!fUndoHistory.empty())
|
||||||
fEditAtSave = fUndoHistory.Top();
|
fEditAtSave = fUndoHistory.top();
|
||||||
|
|
||||||
_NotifyListeners();
|
_NotifyListeners();
|
||||||
}
|
}
|
||||||
@@ -149,9 +152,9 @@ EditManager::Save()
|
|||||||
bool
|
bool
|
||||||
EditManager::IsSaved()
|
EditManager::IsSaved()
|
||||||
{
|
{
|
||||||
bool saved = fUndoHistory.IsEmpty();
|
bool saved = fUndoHistory.empty();
|
||||||
if (fEditAtSave.IsSet() && !saved) {
|
if (fEditAtSave.IsSet() && !saved) {
|
||||||
if (fEditAtSave == fUndoHistory.Top())
|
if (fEditAtSave == fUndoHistory.top())
|
||||||
saved = true;
|
saved = true;
|
||||||
}
|
}
|
||||||
return saved;
|
return saved;
|
||||||
@@ -161,17 +164,20 @@ EditManager::IsSaved()
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
bool
|
void
|
||||||
EditManager::AddListener(Listener* listener)
|
EditManager::AddListener(Listener* listener)
|
||||||
{
|
{
|
||||||
return fListeners.Add(listener);
|
return fListeners.push_back(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
EditManager::RemoveListener(Listener* listener)
|
EditManager::RemoveListener(Listener* listener)
|
||||||
{
|
{
|
||||||
fListeners.Remove(listener);
|
fListeners.erase(std::remove(
|
||||||
|
fListeners.begin(),
|
||||||
|
fListeners.end(),
|
||||||
|
listener), fListeners.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -184,24 +190,24 @@ EditManager::_AddEdit(const UndoableEditRef& edit)
|
|||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
|
|
||||||
bool add = true;
|
bool add = true;
|
||||||
if (!fUndoHistory.IsEmpty()) {
|
if (!fUndoHistory.empty()) {
|
||||||
// Try to collapse edits to a single edit
|
// Try to collapse edits to a single edit
|
||||||
// or remove this and the previous edit if
|
// or remove this and the previous edit if
|
||||||
// they reverse each other
|
// they reverse each other
|
||||||
const UndoableEditRef& top = fUndoHistory.Top();
|
const UndoableEditRef& top = fUndoHistory.top();
|
||||||
if (edit->UndoesPrevious(top.Get())) {
|
if (edit->UndoesPrevious(top.Get())) {
|
||||||
add = false;
|
add = false;
|
||||||
fUndoHistory.Pop();
|
fUndoHistory.pop();
|
||||||
} else if (top->CombineWithNext(edit.Get())) {
|
} else if (top->CombineWithNext(edit.Get())) {
|
||||||
add = false;
|
add = false;
|
||||||
// After collapsing, the edit might
|
// After collapsing, the edit might
|
||||||
// have changed it's mind about InitCheck()
|
// have changed it's mind about InitCheck()
|
||||||
// (the commands reversed each other)
|
// (the commands reversed each other)
|
||||||
if (top->InitCheck() != B_OK) {
|
if (top->InitCheck() != B_OK) {
|
||||||
fUndoHistory.Pop();
|
fUndoHistory.pop();
|
||||||
}
|
}
|
||||||
} else if (edit->CombineWithPrevious(top.Get())) {
|
} else if (edit->CombineWithPrevious(top.Get())) {
|
||||||
fUndoHistory.Pop();
|
fUndoHistory.pop();
|
||||||
// After collapsing, the edit might
|
// After collapsing, the edit might
|
||||||
// have changed it's mind about InitCheck()
|
// have changed it's mind about InitCheck()
|
||||||
// (the commands reversed each other)
|
// (the commands reversed each other)
|
||||||
@@ -210,16 +216,14 @@ EditManager::_AddEdit(const UndoableEditRef& edit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (add) {
|
if (add)
|
||||||
if (!fUndoHistory.Push(edit))
|
fUndoHistory.push(edit);
|
||||||
status = B_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
// The redo stack needs to be empty
|
// The redo stack needs to be empty
|
||||||
// as soon as an edit was added (also in case of collapsing)
|
// as soon as an edit was added (also in case of collapsing)
|
||||||
while (!fRedoHistory.IsEmpty()) {
|
while (!fRedoHistory.empty()) {
|
||||||
fRedoHistory.Pop();
|
fRedoHistory.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,15 +234,14 @@ EditManager::_AddEdit(const UndoableEditRef& edit)
|
|||||||
void
|
void
|
||||||
EditManager::_NotifyListeners()
|
EditManager::_NotifyListeners()
|
||||||
{
|
{
|
||||||
int32 count = fListeners.CountItems();
|
|
||||||
if (count == 0)
|
|
||||||
return;
|
|
||||||
// Iterate a copy of the list, so we don't crash if listeners
|
// Iterate a copy of the list, so we don't crash if listeners
|
||||||
// detach themselves while being notified.
|
// detach themselves while being notified.
|
||||||
ListenerList listenersCopy(fListeners);
|
std::vector<Listener*> listeners(fListeners);
|
||||||
if (listenersCopy.CountItems() != count)
|
|
||||||
return;
|
std::vector<Listener*>::const_iterator it;
|
||||||
for (int32 i = 0; i < count; i++)
|
for (it = listeners.begin(); it != listeners.end(); it++) {
|
||||||
listenersCopy.ItemAtFast(i)->EditManagerChanged(this);
|
Listener* listener = *it;
|
||||||
|
listener->EditManagerChanged(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2012, Stephan Aßmus <[email protected]>
|
* Copyright 2006-2012, Stephan Aßmus <[email protected]>
|
||||||
|
* Copyright 2021, Andrew Lindesay <[email protected]>
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef EDIT_MANAGER_H
|
#ifndef EDIT_MANAGER_H
|
||||||
#define EDIT_MANAGER_H
|
#define EDIT_MANAGER_H
|
||||||
|
|
||||||
#include "EditStack.h"
|
#include <vector>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
#include "UndoableEdit.h"
|
#include "UndoableEdit.h"
|
||||||
|
|
||||||
class BString;
|
class BString;
|
||||||
@@ -40,7 +43,7 @@ public:
|
|||||||
void Save();
|
void Save();
|
||||||
bool IsSaved();
|
bool IsSaved();
|
||||||
|
|
||||||
bool AddListener(Listener* listener);
|
void AddListener(Listener* listener);
|
||||||
void RemoveListener(Listener* listener);
|
void RemoveListener(Listener* listener);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -49,12 +52,13 @@ private:
|
|||||||
void _NotifyListeners();
|
void _NotifyListeners();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EditStack fUndoHistory;
|
std::stack<UndoableEditRef>
|
||||||
EditStack fRedoHistory;
|
fUndoHistory;
|
||||||
|
std::stack<UndoableEditRef>
|
||||||
|
fRedoHistory;
|
||||||
UndoableEditRef fEditAtSave;
|
UndoableEditRef fEditAtSave;
|
||||||
|
std::vector<Listener*>
|
||||||
typedef List<Listener*, true, 4> ListenerList;
|
fListeners;
|
||||||
ListenerList fListeners;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // EDIT_MANAGER_H
|
#endif // EDIT_MANAGER_H
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012, Stephan Aßmus <[email protected]>
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "EditStack.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
||||||
EditStack::EditStack()
|
|
||||||
: fEdits()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EditStack::~EditStack()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
EditStack::Push(const UndoableEditRef& edit)
|
|
||||||
{
|
|
||||||
return fEdits.Add(edit);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
UndoableEditRef
|
|
||||||
EditStack::Pop()
|
|
||||||
{
|
|
||||||
UndoableEditRef edit(Top());
|
|
||||||
fEdits.Remove();
|
|
||||||
return edit;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const UndoableEditRef&
|
|
||||||
EditStack::Top() const
|
|
||||||
{
|
|
||||||
return fEdits.LastItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
EditStack::IsEmpty() const
|
|
||||||
{
|
|
||||||
return fEdits.CountItems() == 0;
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012, Stephan Aßmus <[email protected]>
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef EDIT_STACK_H
|
|
||||||
#define EDIT_STACK_H
|
|
||||||
|
|
||||||
#include "List.h"
|
|
||||||
#include "UndoableEdit.h"
|
|
||||||
|
|
||||||
class EditStack {
|
|
||||||
public:
|
|
||||||
EditStack();
|
|
||||||
virtual ~EditStack();
|
|
||||||
|
|
||||||
bool Push(const UndoableEditRef& edit);
|
|
||||||
UndoableEditRef Pop();
|
|
||||||
|
|
||||||
const UndoableEditRef& Top() const;
|
|
||||||
|
|
||||||
bool IsEmpty() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef List<UndoableEditRef, false> EditList;
|
|
||||||
|
|
||||||
EditList fEdits;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // EDIT_STACK_H
|
|
||||||
@@ -11,22 +11,15 @@
|
|||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
//static int32 sInstanceCount = 0;
|
|
||||||
|
|
||||||
|
|
||||||
UndoableEdit::UndoableEdit()
|
UndoableEdit::UndoableEdit()
|
||||||
:
|
:
|
||||||
fTimeStamp(system_time())
|
fTimeStamp(system_time())
|
||||||
{
|
{
|
||||||
// sInstanceCount++;
|
|
||||||
// printf("UndoableEdits: %ld\n", sInstanceCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UndoableEdit::~UndoableEdit()
|
UndoableEdit::~UndoableEdit()
|
||||||
{
|
{
|
||||||
// sInstanceCount--;
|
|
||||||
// printf("UndoableEdits: %ld\n", sInstanceCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user