mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-02 13:01:18 +12:00
UI: Use wxListView instead of wxListCtrl (#1584)
This commit is contained in:
parent
3eff2d4a60
commit
2eec6b44c3
14 changed files with 61 additions and 65 deletions
|
@ -5,6 +5,7 @@
|
|||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "gui/helpers/wxHelpers.h"
|
||||
|
@ -79,7 +80,7 @@ MemorySearcherTool::MemorySearcherTool(wxFrame* parent)
|
|||
m_gauge->Enable(false);
|
||||
|
||||
m_textEntryTable = new wxStaticText(this, wxID_ANY, _("Results"));
|
||||
m_listResults = new wxListCtrl(this, LIST_RESULTS, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SORT_ASCENDING);
|
||||
m_listResults = new wxListView(this, LIST_RESULTS, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SORT_ASCENDING);
|
||||
m_listResults->Bind(wxEVT_LEFT_DCLICK, &MemorySearcherTool::OnResultListClick, this);
|
||||
{
|
||||
wxListItem col0;
|
||||
|
@ -388,14 +389,8 @@ void MemorySearcherTool::OnEntryListRightClick(wxDataViewEvent& event)
|
|||
|
||||
void MemorySearcherTool::OnResultListClick(wxMouseEvent& event)
|
||||
{
|
||||
long selectedIndex = -1;
|
||||
|
||||
while (true)
|
||||
for (long selectedIndex = m_listResults->GetFirstSelected(); selectedIndex != wxNOT_FOUND; selectedIndex = m_listResults->GetNextSelected(selectedIndex))
|
||||
{
|
||||
selectedIndex = m_listResults->GetNextItem(selectedIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
if (selectedIndex == wxNOT_FOUND)
|
||||
break;
|
||||
|
||||
long address = m_listResults->GetItemData(selectedIndex);
|
||||
auto currValue = m_listResults->GetItemText(selectedIndex, 1);
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ wxDECLARE_EVENT_TABLE();
|
|||
wxComboBox* m_cbDataType;
|
||||
wxTextCtrl* m_textValue;
|
||||
wxButton *m_buttonStart, *m_buttonFilter;
|
||||
wxListCtrl* m_listResults;
|
||||
wxListView* m_listResults;
|
||||
wxDataViewListCtrl* m_listEntryTable;
|
||||
wxStaticText* m_textEntryTable;
|
||||
wxGauge* m_gauge;
|
||||
|
|
|
@ -25,9 +25,8 @@
|
|||
|
||||
wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent);
|
||||
|
||||
|
||||
wxDownloadManagerList::wxDownloadManagerList(wxWindow* parent, wxWindowID id)
|
||||
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
{
|
||||
AddColumns();
|
||||
|
||||
|
@ -52,7 +51,7 @@ wxDownloadManagerList::wxDownloadManagerList(wxWindow* parent, wxWindowID id)
|
|||
|
||||
boost::optional<const wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetSelectedTitleEntry() const
|
||||
{
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto tmp = GetTitleEntry(selection);
|
||||
|
@ -65,7 +64,7 @@ boost::optional<const wxDownloadManagerList::TitleEntry&> wxDownloadManagerList:
|
|||
|
||||
boost::optional<wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetSelectedTitleEntry()
|
||||
{
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto tmp = GetTitleEntry(selection);
|
||||
|
@ -324,7 +323,7 @@ void wxDownloadManagerList::OnContextMenu(wxContextMenuEvent& event)
|
|||
wxMenu menu;
|
||||
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxDownloadManagerList::OnContextMenuSelected, this);
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
@ -380,7 +379,7 @@ void wxDownloadManagerList::OnContextMenuSelected(wxCommandEvent& event)
|
|||
if (m_context_worker.valid() && !future_is_ready(m_context_worker))
|
||||
return;
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class wxDownloadManagerList : public wxListCtrl
|
||||
class wxDownloadManagerList : public wxListView
|
||||
{
|
||||
friend class TitleManager;
|
||||
public:
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <numeric>
|
||||
|
||||
#include <wx/listctrl.h>
|
||||
#include <wx/wupdlock.h>
|
||||
#include <wx/menu.h>
|
||||
#include <wx/mstream.h>
|
||||
|
@ -133,7 +134,7 @@ bool writeICNS(const fs::path& pngPath, const fs::path& icnsPath) {
|
|||
}
|
||||
|
||||
wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
|
||||
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, GetStyleFlags(Style::kList)), m_style(Style::kList)
|
||||
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, GetStyleFlags(Style::kList)), m_style(Style::kList)
|
||||
{
|
||||
const auto& config = GetConfig();
|
||||
|
||||
|
@ -393,7 +394,7 @@ void wxGameList::SetStyle(Style style, bool save)
|
|||
SetWindowStyleFlag(GetStyleFlags(m_style));
|
||||
|
||||
uint64 selected_title_id = 0;
|
||||
auto selection = GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
selected_title_id = (uint64)GetItemData(selection);
|
||||
|
@ -416,8 +417,8 @@ void wxGameList::SetStyle(Style style, bool save)
|
|||
|
||||
if(selection != wxNOT_FOUND)
|
||||
{
|
||||
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
EnsureVisible(selection);
|
||||
Select(selection);
|
||||
Focus(selection);
|
||||
}
|
||||
|
||||
if(save)
|
||||
|
@ -549,15 +550,14 @@ void wxGameList::OnKeyDown(wxListEvent& event)
|
|||
const auto item_count = GetItemCount();
|
||||
if (item_count > 0)
|
||||
{
|
||||
auto selection = (int)GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
auto selection = (int)GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
selection = 0;
|
||||
else
|
||||
selection = std::max(0, selection - GetCountPerPage());
|
||||
|
||||
SetItemState(wxNOT_FOUND, 0, wxLIST_STATE_SELECTED);
|
||||
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
EnsureVisible(selection);
|
||||
Select(selection);
|
||||
Focus(selection);
|
||||
}
|
||||
}
|
||||
else if (keycode == WXK_RIGHT)
|
||||
|
@ -565,15 +565,14 @@ void wxGameList::OnKeyDown(wxListEvent& event)
|
|||
const auto item_count = GetItemCount();
|
||||
if (item_count > 0)
|
||||
{
|
||||
auto selection = (int)GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
auto selection = (int)GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
selection = 0;
|
||||
|
||||
selection = std::min(item_count - 1, selection + GetCountPerPage());
|
||||
|
||||
SetItemState(wxNOT_FOUND, 0, wxLIST_STATE_SELECTED);
|
||||
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
EnsureVisible(selection);
|
||||
Select(selection);
|
||||
Focus(selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -613,7 +612,7 @@ void wxGameList::OnContextMenu(wxContextMenuEvent& event)
|
|||
wxMenu menu;
|
||||
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxGameList::OnContextMenuSelected, this);
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto title_id = (uint64)GetItemData(selection);
|
||||
|
|
|
@ -30,7 +30,7 @@ wxDECLARE_EVENT(wxEVT_OPEN_GRAPHIC_PACK, wxTitleIdEvent);
|
|||
wxDECLARE_EVENT(wxEVT_GAMELIST_BEGIN_UPDATE, wxCommandEvent);
|
||||
wxDECLARE_EVENT(wxEVT_GAMELIST_END_UPDATE, wxCommandEvent);
|
||||
|
||||
class wxGameList : public wxListCtrl
|
||||
class wxGameList : public wxListView
|
||||
{
|
||||
friend class MainWindow;
|
||||
public:
|
||||
|
|
|
@ -38,7 +38,7 @@ wxDEFINE_EVENT(wxEVT_TITLE_REMOVED, wxCommandEvent);
|
|||
wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent);
|
||||
|
||||
wxTitleManagerList::wxTitleManagerList(wxWindow* parent, wxWindowID id)
|
||||
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
{
|
||||
AddColumns();
|
||||
|
||||
|
@ -74,7 +74,7 @@ wxTitleManagerList::~wxTitleManagerList()
|
|||
|
||||
boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry() const
|
||||
{
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto tmp = GetTitleEntry(selection);
|
||||
|
@ -87,7 +87,7 @@ boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSe
|
|||
|
||||
boost::optional<wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry()
|
||||
{
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto tmp = GetTitleEntry(selection);
|
||||
|
@ -757,7 +757,7 @@ void wxTitleManagerList::OnContextMenu(wxContextMenuEvent& event)
|
|||
wxMenu menu;
|
||||
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxTitleManagerList::OnContextMenuSelected, this);
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
@ -856,7 +856,7 @@ void wxTitleManagerList::OnContextMenuSelected(wxCommandEvent& event)
|
|||
if (m_context_worker.valid() && !future_is_ready(m_context_worker))
|
||||
return;
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class wxTitleManagerList : public wxListCtrl
|
||||
class wxTitleManagerList : public wxListView
|
||||
{
|
||||
friend class TitleManager;
|
||||
public:
|
||||
|
|
|
@ -230,8 +230,8 @@ void BreakpointWindow::OnRightDown(wxMouseEvent& event)
|
|||
}
|
||||
else
|
||||
{
|
||||
m_breakpoints->SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
|
||||
m_breakpoints->SetItemState(index, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
||||
m_breakpoints->Focus(index);
|
||||
m_breakpoints->Select(index);
|
||||
|
||||
wxMenu menu;
|
||||
menu.Append(MENU_ID_DELETE_BP, _("Delete breakpoint"));
|
||||
|
@ -245,7 +245,7 @@ void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt)
|
|||
{
|
||||
if (evt.GetId() == MENU_ID_DELETE_BP)
|
||||
{
|
||||
long sel = m_breakpoints->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
long sel = m_breakpoints->GetFirstSelected();
|
||||
if (sel != wxNOT_FOUND)
|
||||
{
|
||||
if (sel >= debuggerState.breakpoints.size())
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "gui/guiWrapper.h"
|
||||
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
|
||||
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
enum ItemColumns
|
||||
{
|
||||
|
@ -10,8 +11,7 @@ enum ItemColumns
|
|||
ColumnModule,
|
||||
};
|
||||
|
||||
SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size) :
|
||||
wxListCtrl(parent, id, pos, size, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size) : wxListView(parent, id, pos, size, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
{
|
||||
wxListItem col0;
|
||||
col0.SetId(ColumnName);
|
||||
|
@ -106,7 +106,7 @@ wxString SymbolListCtrl::OnGetItemText(long item, long column) const
|
|||
|
||||
void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
|
||||
{
|
||||
long selected = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
long selected = GetFirstSelected();
|
||||
if (selected == wxNOT_FOUND)
|
||||
return;
|
||||
const auto text = GetItemText(selected, ColumnAddress);
|
||||
|
@ -119,7 +119,7 @@ void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
|
|||
|
||||
void SymbolListCtrl::OnRightClick(wxListEvent& event)
|
||||
{
|
||||
long selected = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
long selected = GetFirstSelected();
|
||||
if (selected == wxNOT_FOUND)
|
||||
return;
|
||||
auto text = GetItemText(selected, ColumnAddress);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
class SymbolListCtrl : public wxListCtrl
|
||||
class SymbolListCtrl : public wxListView
|
||||
{
|
||||
public:
|
||||
SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <cinttypes>
|
||||
#include <helpers/wxHelpers.h>
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -42,7 +43,7 @@ DebugPPCThreadsWindow::DebugPPCThreadsWindow(wxFrame& parent)
|
|||
wxFrame::SetBackgroundColour(*wxWHITE);
|
||||
|
||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_thread_list = new wxListCtrl(this, GPLIST_ID, wxPoint(0, 0), wxSize(930, 240), wxLC_REPORT);
|
||||
m_thread_list = new wxListView(this, GPLIST_ID, wxPoint(0, 0), wxSize(930, 240), wxLC_REPORT);
|
||||
|
||||
m_thread_list->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Courier New")); //wxSystemSettings::GetFont(wxSYS_OEM_FIXED_FONT));
|
||||
|
||||
|
@ -169,7 +170,7 @@ void DebugPPCThreadsWindow::RefreshThreadList()
|
|||
wxWindowUpdateLocker lock(m_thread_list);
|
||||
|
||||
long selected_thread = 0;
|
||||
const int selection = m_thread_list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const int selection = m_thread_list->GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
selected_thread = m_thread_list->GetItemData(selection);
|
||||
|
||||
|
@ -267,8 +268,11 @@ void DebugPPCThreadsWindow::RefreshThreadList()
|
|||
|
||||
m_thread_list->SetItem(i, 12, tempStr);
|
||||
|
||||
if(selected_thread != 0 && selected_thread == (long)threadItrMPTR)
|
||||
m_thread_list->SetItemState(i, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
|
||||
if (selected_thread != 0 && selected_thread == (long)threadItrMPTR)
|
||||
{
|
||||
m_thread_list->Select(i);
|
||||
m_thread_list->Focus(i);
|
||||
}
|
||||
}
|
||||
srwlock_activeThreadList.UnlockWrite();
|
||||
__OSUnlockScheduler();
|
||||
|
@ -436,12 +440,11 @@ void DebugPPCThreadsWindow::OnThreadListRightClick(wxMouseEvent& event)
|
|||
if (itemIndex == wxNOT_FOUND)
|
||||
return;
|
||||
// select item
|
||||
m_thread_list->SetItemState(itemIndex, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
|
||||
long sel = m_thread_list->GetNextItem(-1, wxLIST_NEXT_ALL,
|
||||
wxLIST_STATE_SELECTED);
|
||||
m_thread_list->Focus(itemIndex);
|
||||
long sel = m_thread_list->GetFirstSelected();
|
||||
if (sel != wxNOT_FOUND)
|
||||
m_thread_list->SetItemState(sel, 0, wxLIST_STATE_SELECTED);
|
||||
m_thread_list->SetItemState(itemIndex, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
||||
m_thread_list->Select(sel, false);
|
||||
m_thread_list->Select(itemIndex);
|
||||
// check if thread is still on the list of active threads
|
||||
MPTR threadMPTR = (MPTR)m_thread_list->GetItemData(itemIndex);
|
||||
__OSLockScheduler();
|
||||
|
|
|
@ -23,7 +23,7 @@ private:
|
|||
void PresentProfileResults(OSThread_t* thread, const std::unordered_map<VAddr, uint32>& samples);
|
||||
void DumpStackTrace(struct OSThread_t* thread);
|
||||
|
||||
wxListCtrl* m_thread_list;
|
||||
wxListView* m_thread_list;
|
||||
wxCheckBox* m_auto_refresh;
|
||||
wxTimer* m_timer;
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ DECLARE_EXPORTED_EVENT_TYPE(WXEXPORT, wxEVT_COMMAND_LIST_ITEM_UNCHECKED, -1);
|
|||
|
||||
//! This is the class which performs all transactions with the server.
|
||||
//! It uses the wxSocket facilities.
|
||||
class wxCheckedListCtrl : public wxListCtrl
|
||||
class wxCheckedListCtrl : public wxListView
|
||||
{
|
||||
protected:
|
||||
|
||||
|
@ -85,7 +85,7 @@ protected:
|
|||
|
||||
public:
|
||||
wxCheckedListCtrl()
|
||||
: wxListCtrl(), m_imageList(16, 16, TRUE) {}
|
||||
: wxListView(), m_imageList(16, 16, TRUE) {}
|
||||
|
||||
wxCheckedListCtrl(wxWindow *parent, wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pt = wxDefaultPosition,
|
||||
|
@ -93,7 +93,7 @@ public:
|
|||
long style = wxCLC_CHECK_WHEN_SELECTING,
|
||||
const wxValidator& validator = wxDefaultValidator,
|
||||
const wxString& name = wxListCtrlNameStr)
|
||||
: wxListCtrl(), m_imageList(16, 16, TRUE)
|
||||
: wxListView(), m_imageList(16, 16, TRUE)
|
||||
{ Create(parent, id, pt, sz, style, validator, name); }
|
||||
|
||||
bool Create(wxWindow *parent, wxWindowID id = wxID_ANY,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue