mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Implement a table widget with the list of the splits of one account, and open this if an account in the tree is double-clicked.
Date and values/amounts can follow next, once those types are suitably wrapped into C++ as well. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18868 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
63fc328029
commit
cad5dc6214
@ -50,6 +50,11 @@ public:
|
||||
QString getCode() const { return QString::fromUtf8(xaccAccountGetCode(get())); }
|
||||
QString getDescription() const { return QString::fromUtf8(xaccAccountGetDescription(get())); }
|
||||
|
||||
::SplitList* getSplitList() const { return xaccAccountGetSplitList(get()); }
|
||||
|
||||
/** @name Account tree traversal */
|
||||
//@{
|
||||
|
||||
Account get_parent() const { return gnc_account_get_parent(get()); }
|
||||
Account get_root() { return gnc_account_get_root(get()); }
|
||||
bool is_root() const { return gnc_account_is_root(get()); }
|
||||
@ -73,6 +78,7 @@ public:
|
||||
|
||||
gint get_current_depth () const { return gnc_account_get_current_depth(get()); }
|
||||
gint get_tree_depth () const { return gnc_account_get_tree_depth(get()); }
|
||||
//@}
|
||||
|
||||
|
||||
typedef QList< ::Account*> AccountQList;
|
||||
|
@ -27,8 +27,8 @@ namespace gnc
|
||||
{
|
||||
|
||||
AccountTreeModel::AccountTreeModel(Account rootaccount, QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_root(rootaccount)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_root(rootaccount)
|
||||
{
|
||||
}
|
||||
|
||||
@ -108,14 +108,14 @@ QVariant AccountTreeModel::data(const QModelIndex& index, int role) const
|
||||
Account account(static_cast< ::Account*>(index.internalPointer()));
|
||||
switch (index.column())
|
||||
{
|
||||
case 0:
|
||||
return account.getName();
|
||||
case 1:
|
||||
return account.getCode();
|
||||
case 2:
|
||||
return account.getDescription();
|
||||
default:
|
||||
return QVariant();
|
||||
case 0:
|
||||
return account.getName();
|
||||
case 1:
|
||||
return account.getCode();
|
||||
case 2:
|
||||
return account.getDescription();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -141,14 +141,14 @@ QVariant AccountTreeModel::headerData(int section, Qt::Orientation orientation,
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case 0:
|
||||
return QString("Name");
|
||||
case 1:
|
||||
return QString("Code");
|
||||
case 2:
|
||||
return QString("Description");
|
||||
default:
|
||||
return QVariant();
|
||||
case 0:
|
||||
return QString("Name");
|
||||
case 1:
|
||||
return QString("Code");
|
||||
case 2:
|
||||
return QString("Description");
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -162,11 +162,10 @@ QModelIndex AccountListModel::index(int row, int column,
|
||||
const QModelIndex &parent) const
|
||||
{
|
||||
//qDebug() << "index(), " << row << column << parent;
|
||||
if (!hasIndex(row, column, parent)
|
||||
|| row >= m_acclist.size())
|
||||
if (!hasIndex(row, column, parent) || row >= m_list.size())
|
||||
return QModelIndex();
|
||||
|
||||
Account childItem = m_acclist.at(row);
|
||||
Account childItem = m_list.at(row);
|
||||
if (childItem.get())
|
||||
{
|
||||
//qDebug() << "returning" << childItem.getName();
|
||||
|
@ -72,12 +72,12 @@ class AccountListModel : public AccountTreeModel
|
||||
Q_OBJECT
|
||||
public:
|
||||
AccountListModel(Account rootaccount, QObject *parent = 0)
|
||||
: AccountTreeModel(rootaccount, parent)
|
||||
, m_acclist(Account::fromGList(rootaccount.get_descendants()))
|
||||
: AccountTreeModel(rootaccount, parent)
|
||||
, m_list(Account::fromGList(rootaccount.get_descendants()))
|
||||
{
|
||||
}
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const { return m_acclist.size(); }
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const { return m_list.size(); }
|
||||
|
||||
QModelIndex index(int row, int column,
|
||||
const QModelIndex &parent = QModelIndex()) const;
|
||||
@ -85,7 +85,7 @@ public:
|
||||
QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); }
|
||||
|
||||
private:
|
||||
Account::AccountQList m_acclist;
|
||||
Account::AccountQList m_list;
|
||||
};
|
||||
|
||||
|
||||
|
@ -13,18 +13,22 @@ SET (gnc_SOURCES
|
||||
AccountItemModel.cpp
|
||||
Book.cpp
|
||||
Session.cpp
|
||||
SplitListModel.cpp
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
)
|
||||
|
||||
SET (gnc_QOBJECT_HEADERS
|
||||
AccountItemModel.hpp
|
||||
SplitListModel.hpp
|
||||
mainwindow.hpp
|
||||
)
|
||||
SET (gnc_HEADERS ${gnc_QOBJECT_HEADERS}
|
||||
Account.hpp
|
||||
Book.hpp
|
||||
Session.hpp
|
||||
Split.hpp
|
||||
Transaction.hpp
|
||||
WeakPointer.hpp
|
||||
)
|
||||
|
||||
|
103
src/gnc/Split.hpp
Normal file
103
src/gnc/Split.hpp
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Split.hpp
|
||||
* Copyright (C) 2010 Christian Stimming
|
||||
*
|
||||
* 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 2 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, contact:
|
||||
*
|
||||
* Free Software Foundation Voice: +1-617-542-5942
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
*/
|
||||
|
||||
#ifndef GNC_SPLIT_HPP
|
||||
#define GNC_SPLIT_HPP
|
||||
|
||||
// gnucash includes
|
||||
#include "config.h"
|
||||
extern "C"
|
||||
{
|
||||
#include "qof.h"
|
||||
#include "engine/Split.h"
|
||||
}
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
namespace gnc
|
||||
{
|
||||
class Split;
|
||||
typedef QList< ::Split*> SplitQList;
|
||||
}
|
||||
|
||||
#include "gnc/WeakPointer.hpp"
|
||||
#include "gnc/Account.hpp"
|
||||
#include "gnc/Book.hpp"
|
||||
#include "gnc/Transaction.hpp"
|
||||
|
||||
namespace gnc
|
||||
{
|
||||
|
||||
class Split : public WeakPointer< ::Split >
|
||||
{
|
||||
public:
|
||||
typedef WeakPointer< ::Split > base_class;
|
||||
Split(element_type* ptr = 0)
|
||||
: base_class(ptr)
|
||||
{ }
|
||||
|
||||
Book getBook() const { return xaccSplitGetBook(get()); }
|
||||
Account getAccount() const { return xaccSplitGetAccount(get()); }
|
||||
void setAccount(Account& acc) { xaccSplitSetAccount(get(), acc.get()); }
|
||||
|
||||
Transaction getParent() const { return xaccSplitGetParent(get()); }
|
||||
void setParent(Transaction& trans) { xaccSplitSetParent(get(), trans.get()); }
|
||||
|
||||
QString getMemo() const { return QString::fromUtf8(xaccSplitGetMemo(get())); }
|
||||
void setMemo(const QString& v) { xaccSplitSetMemo(get(), v.toUtf8()); }
|
||||
|
||||
QString getAction() const { return QString::fromUtf8(xaccSplitGetAction(get())); }
|
||||
void setAction(const QString& v) { xaccSplitSetAction(get(), v.toUtf8()); }
|
||||
|
||||
char getReconcile() const { return xaccSplitGetReconcile(get()); }
|
||||
void setReconcile(char v) { xaccSplitSetReconcile(get(), v); }
|
||||
|
||||
Split getOtherSplit() const { return xaccSplitGetOtherSplit(get()); }
|
||||
|
||||
QString getCorrAccountFullName() const
|
||||
{
|
||||
char * r = xaccSplitGetCorrAccountFullName(get());
|
||||
QString result = QString::fromUtf8(r);
|
||||
g_free (r);
|
||||
return result;
|
||||
}
|
||||
QString getCorrAccountName() const { return QString::fromUtf8(xaccSplitGetCorrAccountName(get())); }
|
||||
QString getCorrAccountCode() const { return QString::fromUtf8(xaccSplitGetCorrAccountCode(get())); }
|
||||
|
||||
|
||||
static SplitQList fromGList(GList* glist)
|
||||
{
|
||||
SplitQList result;
|
||||
GList* list = glist;
|
||||
while (list)
|
||||
{
|
||||
result.append(reinterpret_cast< ::Split*>(list->data));
|
||||
list = g_list_next(list);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // END namespace gnc
|
||||
|
||||
#endif
|
124
src/gnc/SplitListModel.cpp
Normal file
124
src/gnc/SplitListModel.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* SplitListModel.hpp
|
||||
* Copyright (C) 2010 Christian Stimming
|
||||
*
|
||||
* 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 2 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, contact:
|
||||
*
|
||||
* Free Software Foundation Voice: +1-617-542-5942
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
*/
|
||||
|
||||
#include "SplitListModel.hpp"
|
||||
#include <QDebug>
|
||||
|
||||
namespace gnc
|
||||
{
|
||||
|
||||
SplitListModel::SplitListModel(const SplitQList splits, QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_list(splits)
|
||||
{
|
||||
}
|
||||
|
||||
QModelIndex SplitListModel::index(int row, int column,
|
||||
const QModelIndex &parent) const
|
||||
{
|
||||
//qDebug() << "index(), " << row << column << parent;
|
||||
if (!hasIndex(row, column, parent) || row >= m_list.size())
|
||||
return QModelIndex();
|
||||
|
||||
Split childItem = m_list.at(row);
|
||||
if (childItem.get())
|
||||
{
|
||||
//qDebug() << "returning" << childItem.getName();
|
||||
return createIndex(row, column, childItem.get());
|
||||
}
|
||||
else
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int SplitListModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
//qDebug() << "columnCount()" << parent;
|
||||
// if (!parent.isValid())
|
||||
// return 0;
|
||||
// else
|
||||
return 4; // Fixed number for now
|
||||
}
|
||||
|
||||
QVariant SplitListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
//qDebug() << "data(), " << index;
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
Split split(static_cast< ::Split*>(index.internalPointer()));
|
||||
Transaction trans(split.getParent());
|
||||
switch (index.column())
|
||||
{
|
||||
case 0:
|
||||
return trans.getNum();
|
||||
case 1:
|
||||
return trans.getDescription();
|
||||
case 2:
|
||||
return split.getCorrAccountFullName();
|
||||
case 3:
|
||||
return QChar(split.getReconcile());
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags SplitListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
//qDebug() << "flags()" << index;
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
|
||||
// Ensure read-only access only
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
QVariant SplitListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
//qDebug() << "headerData()" << section;
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
if (orientation == Qt::Horizontal)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case 0:
|
||||
return QString("Num");
|
||||
case 1:
|
||||
return QString("Description");
|
||||
case 2:
|
||||
return QString("Account");
|
||||
case 3:
|
||||
return QString("Reconciled?");
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
else
|
||||
return QString("%1").arg(1 + section);
|
||||
}
|
||||
|
||||
|
||||
} // END namespace gnc
|
57
src/gnc/SplitListModel.hpp
Normal file
57
src/gnc/SplitListModel.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* SplitListModel.hpp
|
||||
* Copyright (C) 2010 Christian Stimming
|
||||
*
|
||||
* 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 2 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, contact:
|
||||
*
|
||||
* Free Software Foundation Voice: +1-617-542-5942
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
*/
|
||||
|
||||
#ifndef GNC_SPLITLISTMODEL_HPP
|
||||
#define GNC_SPLITLISTMODEL_HPP
|
||||
|
||||
#include "gnc/Split.hpp"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
namespace gnc
|
||||
{
|
||||
|
||||
/** This is the data model for a list of splits.
|
||||
*/
|
||||
class SplitListModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SplitListModel(const SplitQList splits, QObject *parent = 0);
|
||||
|
||||
QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); }
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const { return m_list.size(); }
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||
QModelIndex index(int row, int column,
|
||||
const QModelIndex &parent = QModelIndex()) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
|
||||
protected:
|
||||
SplitQList m_list;
|
||||
};
|
||||
|
||||
} // END namespace gnc
|
||||
|
||||
#endif
|
68
src/gnc/Transaction.hpp
Normal file
68
src/gnc/Transaction.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Transaction.hpp
|
||||
* Copyright (C) 2010 Christian Stimming
|
||||
*
|
||||
* 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 2 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, contact:
|
||||
*
|
||||
* Free Software Foundation Voice: +1-617-542-5942
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
*/
|
||||
|
||||
#ifndef GNC_TRANSACTION_HPP
|
||||
#define GNC_TRANSACTION_HPP
|
||||
|
||||
// gnucash includes
|
||||
#include "config.h"
|
||||
extern "C"
|
||||
{
|
||||
#include "qof.h"
|
||||
#include "engine/Transaction.h"
|
||||
}
|
||||
|
||||
#include "gnc/WeakPointer.hpp"
|
||||
#include "gnc/Account.hpp"
|
||||
#include "gnc/Book.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
namespace gnc
|
||||
{
|
||||
|
||||
class Transaction : public WeakPointer< ::Transaction >
|
||||
{
|
||||
public:
|
||||
typedef WeakPointer< ::Transaction > base_class;
|
||||
Transaction(element_type* ptr = 0)
|
||||
: base_class(ptr)
|
||||
{ }
|
||||
|
||||
QString getNum() const { return QString::fromUtf8(xaccTransGetNum(get())); }
|
||||
void setNum(const QString& v) { xaccTransSetNum(get(), v.toUtf8()); }
|
||||
|
||||
QString getDescription() const { return QString::fromUtf8(xaccTransGetDescription(get())); }
|
||||
void setDescription(const QString& v) { xaccTransSetDescription(get(), v.toUtf8()); }
|
||||
|
||||
QString getNotes() const { return QString::fromUtf8(xaccTransGetNotes(get())); }
|
||||
void setNotes(const QString& v) { xaccTransSetNotes(get(), v.toUtf8()); }
|
||||
|
||||
int countSplits() const { return xaccTransCountSplits(get()); }
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // END namespace gnc
|
||||
|
||||
#endif
|
@ -46,6 +46,8 @@ extern "C"
|
||||
#include "gnc/Account.hpp"
|
||||
#include "gnc/AccountItemModel.hpp"
|
||||
#include "gnc/Book.hpp"
|
||||
#include "gnc/Split.hpp"
|
||||
#include "gnc/SplitListModel.hpp"
|
||||
|
||||
namespace gnc
|
||||
{
|
||||
@ -173,6 +175,9 @@ void MainWindow::createActions()
|
||||
// ui->actionCut, SLOT(setEnabled(bool)));
|
||||
// connect(ui->textEdit, SIGNAL(copyAvailable(bool)),
|
||||
// ui->actionCopy, SLOT(setEnabled(bool)));
|
||||
|
||||
connect(ui->treeView, SIGNAL(activated(const QModelIndex &)),
|
||||
this, SLOT(activatedAccount(const QModelIndex&)));
|
||||
}
|
||||
|
||||
void MainWindow::createToolBars()
|
||||
@ -246,6 +251,36 @@ QString MainWindow::strippedName(const QString &fullFileName)
|
||||
return QFileInfo(fullFileName).fileName();
|
||||
}
|
||||
|
||||
void MainWindow::activatedAccount(const QModelIndex & index)
|
||||
{
|
||||
if (index.model() != m_accountTreeModel)
|
||||
{
|
||||
qDebug() << "Wrong model";
|
||||
return;
|
||||
}
|
||||
Account account(static_cast< ::Account*>(index.internalPointer()));
|
||||
if (!account)
|
||||
{
|
||||
qDebug() << "Account is null; why?";
|
||||
return;
|
||||
}
|
||||
|
||||
// We have an account, so obtains its list of splits now.
|
||||
::SplitList* slist = account.getSplitList();
|
||||
|
||||
// We create a new model for this list of splits and also a view
|
||||
// widget for this list.
|
||||
QTableView *tableView = new QTableView(this); // FIXME: Parent object unclear
|
||||
SplitListModel *smodel = new SplitListModel(Split::fromGList(slist), tableView);
|
||||
tableView->setModel(smodel);
|
||||
|
||||
// Insert this as a new tab
|
||||
ui->tabWidget->addTab(tableView, account.getName());
|
||||
ui->tabWidget->setCurrentWidget(tableView);
|
||||
|
||||
// Right now it cannot be deleted - this will be implemented later.
|
||||
}
|
||||
|
||||
// ////////////////////////////////////////////////////////////
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
@ -320,24 +355,24 @@ namespace
|
||||
* are in use simultaneously */
|
||||
class progress_functor
|
||||
{
|
||||
public:
|
||||
progress_functor(QProgressBar *progressBar)
|
||||
{
|
||||
m_progressBar = progressBar;
|
||||
}
|
||||
~progress_functor()
|
||||
{
|
||||
m_progressBar = NULL;
|
||||
}
|
||||
static void static_func(const char *message, double percent)
|
||||
{
|
||||
assert(m_progressBar);
|
||||
m_progressBar->setValue(int(percent));
|
||||
// Give the Qt event loop some time
|
||||
qApp->processEvents();
|
||||
}
|
||||
private:
|
||||
static QProgressBar *m_progressBar;
|
||||
public:
|
||||
progress_functor(QProgressBar *progressBar)
|
||||
{
|
||||
m_progressBar = progressBar;
|
||||
}
|
||||
~progress_functor()
|
||||
{
|
||||
m_progressBar = NULL;
|
||||
}
|
||||
static void static_func(const char *message, double percent)
|
||||
{
|
||||
assert(m_progressBar);
|
||||
m_progressBar->setValue(int(percent));
|
||||
// Give the Qt event loop some time
|
||||
qApp->processEvents();
|
||||
}
|
||||
private:
|
||||
static QProgressBar *m_progressBar;
|
||||
};
|
||||
QProgressBar *progress_functor::m_progressBar = NULL;
|
||||
|
||||
|
@ -51,6 +51,7 @@ public:
|
||||
|
||||
public slots:
|
||||
void anchorClicked(const QUrl &);
|
||||
void activatedAccount(const QModelIndex & index);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
Loading…
Reference in New Issue
Block a user