Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer
This commit is contained in:
commit
f7f1e2ce42
@ -233,20 +233,30 @@ cmake_policy(SET CMP0011 NEW)
|
|||||||
find_package(CGAL REQUIRED)
|
find_package(CGAL REQUIRED)
|
||||||
cmake_policy(POP)
|
cmake_policy(POP)
|
||||||
|
|
||||||
set(_cgal_defines "")
|
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp)
|
||||||
if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround
|
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
set(_cgal_defines CGAL_DO_NOT_USE_MPZF)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_library(libslic3r_cgal OBJECT MeshBoolean.cpp MeshBoolean.hpp)
|
# Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options
|
||||||
target_include_directories(libslic3r_cgal PRIVATE
|
# (-frounding-math) still propagate to dependent libs which is not desired.
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
get_target_property(_cgal_tgt CGAL::CGAL ALIASED_TARGET)
|
||||||
$<TARGET_PROPERTY:libigl,INTERFACE_INCLUDE_DIRECTORIES>
|
if (NOT TARGET ${_cgal_tgt})
|
||||||
$<TARGET_PROPERTY:CGAL::CGAL,INTERFACE_INCLUDE_DIRECTORIES>)
|
set (_cgal_tgt CGAL::CGAL)
|
||||||
target_compile_definitions(libslic3r_cgal PRIVATE ${_cgal_defines}
|
endif ()
|
||||||
$<TARGET_PROPERTY:CGAL::CGAL,INTERFACE_COMPILE_DEFINITIONS>)
|
get_target_property(_opts ${_cgal_tgt} INTERFACE_COMPILE_OPTIONS)
|
||||||
target_compile_options(libslic3r_cgal PRIVATE
|
if (_opts)
|
||||||
$<TARGET_PROPERTY:CGAL::CGAL,INTERFACE_COMPILE_OPTIONS>)
|
set(_opts_bad "${_opts}")
|
||||||
|
set(_opts_good "${_opts}")
|
||||||
|
list(FILTER _opts_bad INCLUDE REGEX frounding-math)
|
||||||
|
list(FILTER _opts_good EXCLUDE REGEX frounding-math)
|
||||||
|
set_target_properties(${_cgal_tgt} PROPERTIES INTERFACE_COMPILE_OPTIONS "${_opts_good}")
|
||||||
|
target_compile_options(libslic3r_cgal PRIVATE "${_opts_bad}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} libigl)
|
||||||
|
|
||||||
|
if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround
|
||||||
|
target_compile_definitions(libslic3r_cgal PRIVATE CGAL_DO_NOT_USE_MPZF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
encoding_check(libslic3r)
|
encoding_check(libslic3r)
|
||||||
|
|
||||||
@ -268,7 +278,7 @@ target_link_libraries(libslic3r
|
|||||||
qhull
|
qhull
|
||||||
semver
|
semver
|
||||||
TBB::tbb
|
TBB::tbb
|
||||||
$<TARGET_PROPERTY:CGAL::CGAL,INTERFACE_LINK_LIBRARIES>
|
libslic3r_cgal
|
||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -287,5 +297,3 @@ endif()
|
|||||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||||
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
|
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_sources(libslic3r PRIVATE $<TARGET_OBJECTS:libslic3r_cgal>)
|
|
||||||
|
@ -111,7 +111,7 @@ static TriangleMesh cgal_to_triangle_mesh(const _CGALMesh &cgalmesh)
|
|||||||
auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face));
|
auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face));
|
||||||
int i = 0;
|
int i = 0;
|
||||||
Vec3crd trface;
|
Vec3crd trface;
|
||||||
for (auto v : vtc) trface(i++) = int(v.idx());
|
for (auto v : vtc) trface(i++) = static_cast<unsigned>(v);
|
||||||
facets.emplace_back(trface);
|
facets.emplace_back(trface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,6 +674,7 @@ public:
|
|||||||
set_rotation(Z, rotation);
|
set_rotation(Z, rotation);
|
||||||
set_offset(X, unscale<double>(offs(X)));
|
set_offset(X, unscale<double>(offs(X)));
|
||||||
set_offset(Y, unscale<double>(offs(Y)));
|
set_offset(Y, unscale<double>(offs(Y)));
|
||||||
|
this->object->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -49,12 +49,20 @@ static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image, float scale = 1.
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height)
|
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height, float scale/* = 1.0f*/)
|
||||||
{
|
{
|
||||||
wxBitmap *bitmap = nullptr;
|
wxBitmap *bitmap = nullptr;
|
||||||
auto it = m_map.find(bitmap_key);
|
auto it = m_map.find(bitmap_key);
|
||||||
if (it == m_map.end()) {
|
if (it == m_map.end()) {
|
||||||
bitmap = new wxBitmap(width, height);
|
bitmap = new wxBitmap(width, height);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Contrary to intuition, the `scale` argument isn't "please scale this to such and such"
|
||||||
|
// but rather "the wxImage is sized for backing scale such and such".
|
||||||
|
// So, We need to let the Mac OS wxBitmap implementation
|
||||||
|
// know that the image may already be scaled appropriately for Retina,
|
||||||
|
// and thereby that it's not supposed to upscale it.
|
||||||
|
bitmap->CreateScaled(width, height, -1, scale);
|
||||||
|
#endif
|
||||||
m_map[bitmap_key] = bitmap;
|
m_map[bitmap_key] = bitmap;
|
||||||
} else {
|
} else {
|
||||||
bitmap = it->second;
|
bitmap = it->second;
|
||||||
@ -95,7 +103,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp
|
|||||||
return this->insert(bitmap_key, bmps, bmps + 3);
|
return this->insert(bitmap_key, bmps, bmps + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end)
|
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end, float scale/* = 1.0f*/)
|
||||||
{
|
{
|
||||||
size_t width = 0;
|
size_t width = 0;
|
||||||
size_t height = 0;
|
size_t height = 0;
|
||||||
@ -158,7 +166,13 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
wxBitmap *bitmap = this->insert(bitmap_key, width, height);
|
#ifdef __APPLE__
|
||||||
|
// Note, for this moment width and height are scaled, so divide them by scale to avoid one more multiplication inside CreateScaled()
|
||||||
|
width *= 1.0 / scale;
|
||||||
|
height *= 1.0 / scale;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
wxBitmap *bitmap = this->insert(bitmap_key, width, height, scale);
|
||||||
wxMemoryDC memDC;
|
wxMemoryDC memDC;
|
||||||
memDC.SelectObject(*bitmap);
|
memDC.SelectObject(*bitmap);
|
||||||
memDC.SetBackground(*wxTRANSPARENT_BRUSH);
|
memDC.SetBackground(*wxTRANSPARENT_BRUSH);
|
||||||
@ -167,7 +181,8 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg
|
|||||||
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
|
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
|
||||||
if (bmp->GetWidth() > 0)
|
if (bmp->GetWidth() > 0)
|
||||||
memDC.DrawBitmap(*bmp, x, 0, true);
|
memDC.DrawBitmap(*bmp, x, 0, true);
|
||||||
x += bmp->GetWidth();
|
// we should "move" with step equal to non-scaled width
|
||||||
|
x += bmp->GetWidth()/scale;
|
||||||
}
|
}
|
||||||
memDC.SelectObject(wxNullBitmap);
|
memDC.SelectObject(wxNullBitmap);
|
||||||
return bitmap;
|
return bitmap;
|
||||||
|
@ -23,12 +23,12 @@ public:
|
|||||||
wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; }
|
wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; }
|
||||||
const wxBitmap* find(const std::string &name) const { return const_cast<BitmapCache*>(this)->find(name); }
|
const wxBitmap* find(const std::string &name) const { return const_cast<BitmapCache*>(this)->find(name); }
|
||||||
|
|
||||||
wxBitmap* insert(const std::string &name, size_t width, size_t height);
|
wxBitmap* insert(const std::string &name, size_t width, size_t height, float scale = 1.0f);
|
||||||
wxBitmap* insert(const std::string &name, const wxBitmap &bmp);
|
wxBitmap* insert(const std::string &name, const wxBitmap &bmp);
|
||||||
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2);
|
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2);
|
||||||
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
|
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
|
||||||
wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); }
|
wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps, float scale = 1.0f) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size(), scale); }
|
||||||
wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end);
|
wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end, float scale = 1.0f);
|
||||||
wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, float scale = 1.0f, const bool grayscale = false);
|
wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, float scale = 1.0f, const bool grayscale = false);
|
||||||
|
|
||||||
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
|
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
|
||||||
|
@ -66,7 +66,7 @@ void enable_menu_item(wxUpdateUIEvent& evt, std::function<bool()> const cb_condi
|
|||||||
const auto it = msw_menuitem_bitmaps.find(item->GetId());
|
const auto it = msw_menuitem_bitmaps.find(item->GetId());
|
||||||
if (it != msw_menuitem_bitmaps.end())
|
if (it != msw_menuitem_bitmaps.end())
|
||||||
{
|
{
|
||||||
const wxBitmap& item_icon = create_scaled_bitmap(win, it->second, 16, false, !enable);
|
const wxBitmap& item_icon = create_scaled_bitmap(win, it->second, 16, !enable);
|
||||||
if (item_icon.IsOk())
|
if (item_icon.IsOk())
|
||||||
item->SetBitmap(item_icon);
|
item->SetBitmap(item_icon);
|
||||||
}
|
}
|
||||||
@ -420,41 +420,25 @@ float get_svg_scale_factor(wxWindow *win)
|
|||||||
}
|
}
|
||||||
return win != nullptr ? max_scaling_factor : 1.0f;
|
return win != nullptr ? max_scaling_factor : 1.0f;
|
||||||
#else
|
#else
|
||||||
|
(void)(win);
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true
|
// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true
|
||||||
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in,
|
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in,
|
||||||
const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/, const bool grayscale/* = false*/)
|
const int px_cnt/* = 16*/, const bool grayscale/* = false*/)
|
||||||
{
|
{
|
||||||
static Slic3r::GUI::BitmapCache cache;
|
static Slic3r::GUI::BitmapCache cache;
|
||||||
|
|
||||||
#ifdef __APPLE__
|
const float scale_factor = get_svg_scale_factor(win);
|
||||||
// Note: win->GetContentScaleFactor() is not used anymore here because it tends to
|
|
||||||
// return bogus results quite often (such as 1.0 on Retina or even 0.0).
|
|
||||||
// We're using the max scaling factor across all screens because it's very likely to be good enough.
|
|
||||||
|
|
||||||
static float max_scaling_factor = NAN;
|
unsigned int width = 0;
|
||||||
if (std::isnan(max_scaling_factor)) {
|
unsigned int height = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f);
|
||||||
max_scaling_factor = Slic3r::GUI::mac_max_scaling_factor();
|
|
||||||
}
|
|
||||||
const float scale_factor = win != nullptr ? max_scaling_factor : 1.0f;
|
|
||||||
#else
|
|
||||||
(void)(win);
|
|
||||||
const float scale_factor = 1.0f;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
unsigned int height, width = height = 0;
|
|
||||||
unsigned int& scale_base = is_horizontal ? width : height;
|
|
||||||
|
|
||||||
scale_base = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f);
|
|
||||||
|
|
||||||
std::string bmp_name = bmp_name_in;
|
std::string bmp_name = bmp_name_in;
|
||||||
boost::replace_last(bmp_name, ".png", "");
|
boost::replace_last(bmp_name, ".png", "");
|
||||||
|
|
||||||
// std::string bmp_name = icon_name_respected_to_mode(bmp_name_in);
|
|
||||||
|
|
||||||
// Try loading an SVG first, then PNG if SVG is not found:
|
// Try loading an SVG first, then PNG if SVG is not found:
|
||||||
wxBitmap *bmp = cache.load_svg(bmp_name, width, height, scale_factor, grayscale, Slic3r::GUI::wxGetApp().dark_mode());
|
wxBitmap *bmp = cache.load_svg(bmp_name, width, height, scale_factor, grayscale, Slic3r::GUI::wxGetApp().dark_mode());
|
||||||
if (bmp == nullptr) {
|
if (bmp == nullptr) {
|
||||||
@ -682,7 +666,7 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps()
|
|||||||
for (auto& cat : m_opt_categories)
|
for (auto& cat : m_opt_categories)
|
||||||
bmps.emplace_back( categories_icon.find(cat) == categories_icon.end() ?
|
bmps.emplace_back( categories_icon.find(cat) == categories_icon.end() ?
|
||||||
wxNullBitmap : categories_icon.at(cat));
|
wxNullBitmap : categories_icon.at(cat));
|
||||||
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps);
|
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps, get_svg_scale_factor(m_ctrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_bmp = *bmp;
|
m_bmp = *bmp;
|
||||||
@ -2470,18 +2454,17 @@ void MenuWithSeparators::SetSecondSeparator()
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
ScalableBitmap::ScalableBitmap( wxWindow *parent,
|
ScalableBitmap::ScalableBitmap( wxWindow *parent,
|
||||||
const std::string& icon_name/* = ""*/,
|
const std::string& icon_name/* = ""*/,
|
||||||
const int px_cnt/* = 16*/,
|
const int px_cnt/* = 16*/):
|
||||||
const bool is_horizontal/* = false*/):
|
|
||||||
m_parent(parent), m_icon_name(icon_name),
|
m_parent(parent), m_icon_name(icon_name),
|
||||||
m_px_cnt(px_cnt), m_is_horizontal(is_horizontal)
|
m_px_cnt(px_cnt)
|
||||||
{
|
{
|
||||||
m_bmp = create_scaled_bitmap(parent, icon_name, px_cnt, is_horizontal);
|
m_bmp = create_scaled_bitmap(parent, icon_name, px_cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ScalableBitmap::msw_rescale()
|
void ScalableBitmap::msw_rescale()
|
||||||
{
|
{
|
||||||
m_bmp = create_scaled_bitmap(m_parent, m_icon_name, m_px_cnt, m_is_horizontal);
|
m_bmp = create_scaled_bitmap(m_parent, m_icon_name, m_px_cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -2522,8 +2505,7 @@ ScalableButton::ScalableButton( wxWindow * parent,
|
|||||||
long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) :
|
long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) :
|
||||||
m_parent(parent),
|
m_parent(parent),
|
||||||
m_current_icon_name(bitmap.name()),
|
m_current_icon_name(bitmap.name()),
|
||||||
m_px_cnt(bitmap.px_cnt()),
|
m_px_cnt(bitmap.px_cnt())
|
||||||
m_is_horizontal(bitmap.is_horizontal())
|
|
||||||
{
|
{
|
||||||
Create(parent, id, label, wxDefaultPosition, wxDefaultSize, style);
|
Create(parent, id, label, wxDefaultPosition, wxDefaultSize, style);
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
@ -2554,9 +2536,9 @@ int ScalableButton::GetBitmapHeight()
|
|||||||
|
|
||||||
void ScalableButton::msw_rescale()
|
void ScalableButton::msw_rescale()
|
||||||
{
|
{
|
||||||
SetBitmap(create_scaled_bitmap(m_parent, m_current_icon_name, m_px_cnt, m_is_horizontal));
|
SetBitmap(create_scaled_bitmap(m_parent, m_current_icon_name, m_px_cnt));
|
||||||
if (!m_disabled_icon_name.empty())
|
if (!m_disabled_icon_name.empty())
|
||||||
SetBitmapDisabled(create_scaled_bitmap(m_parent, m_disabled_icon_name, m_px_cnt, m_is_horizontal));
|
SetBitmapDisabled(create_scaled_bitmap(m_parent, m_disabled_icon_name, m_px_cnt));
|
||||||
|
|
||||||
if (m_width > 0 || m_height>0)
|
if (m_width > 0 || m_height>0)
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@ int em_unit(wxWindow* win);
|
|||||||
float get_svg_scale_factor(wxWindow* win);
|
float get_svg_scale_factor(wxWindow* win);
|
||||||
|
|
||||||
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name,
|
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name,
|
||||||
const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false);
|
const int px_cnt = 16, const bool grayscale = false);
|
||||||
|
|
||||||
std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon = false);
|
std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon = false);
|
||||||
void apply_extruder_selector(wxBitmapComboBox** ctrl,
|
void apply_extruder_selector(wxBitmapComboBox** ctrl,
|
||||||
@ -729,8 +729,7 @@ public:
|
|||||||
ScalableBitmap() {};
|
ScalableBitmap() {};
|
||||||
ScalableBitmap( wxWindow *parent,
|
ScalableBitmap( wxWindow *parent,
|
||||||
const std::string& icon_name = "",
|
const std::string& icon_name = "",
|
||||||
const int px_cnt = 16,
|
const int px_cnt = 16);
|
||||||
const bool is_horizontal = false);
|
|
||||||
|
|
||||||
~ScalableBitmap() {}
|
~ScalableBitmap() {}
|
||||||
|
|
||||||
@ -741,14 +740,12 @@ public:
|
|||||||
const std::string& name() const{ return m_icon_name; }
|
const std::string& name() const{ return m_icon_name; }
|
||||||
|
|
||||||
int px_cnt()const {return m_px_cnt;}
|
int px_cnt()const {return m_px_cnt;}
|
||||||
bool is_horizontal()const {return m_is_horizontal;}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxWindow* m_parent{ nullptr };
|
wxWindow* m_parent{ nullptr };
|
||||||
wxBitmap m_bmp = wxBitmap();
|
wxBitmap m_bmp = wxBitmap();
|
||||||
std::string m_icon_name = "";
|
std::string m_icon_name = "";
|
||||||
int m_px_cnt {16};
|
int m_px_cnt {16};
|
||||||
bool m_is_horizontal {false};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -832,7 +829,6 @@ private:
|
|||||||
|
|
||||||
// bitmap dimensions
|
// bitmap dimensions
|
||||||
int m_px_cnt{ 16 };
|
int m_px_cnt{ 16 };
|
||||||
bool m_is_horizontal{ false };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user