Compare commits

...
Author SHA1 Message Date
Hanif Koh 4b8b9abb5e Validate Plugin Symlink Targets Before Replacing Existing Files
A symlink entry's target is now read and checked before anything already
at its destination is removed or renamed aside, so an archive rejected
for its link target leaves the installed plugin files in place.
2026-09-28 16:38:14 +08:00
Hanif Koh 365e4173e1 Harden Archive Extraction Against Symlinks
The plugin installer now creates a symlink entry only when its target is
relative and, joined to the link's own directory, passes
is_path_within_root, via the new is_symlink_target_within_root helper.
Before writing any entry it checks the destination with symlink_status, so
an existing symlink, dangling or not, is replaced rather than followed, and
it creates parent directories inside the existing error handling.
extract_archive_confined replaces a symlink at a destination file the same
way.

is_path_within_root now ignores a trailing separator on the root, which
previously made every path fail the check.
2026-09-28 16:12:30 +08:00
Hanif Koh 6dd8401c59 Confine Updater Archive Extraction to the Target Directory
The preset updater extracted downloaded archives by appending each entry
name to the cache directory, and the network plugin installer did the same
for the plugin folder, without checking that the result stays inside it.

Move the updater's extraction into libslic3r as extract_archive_confined,
which validates every entry with is_path_within_root before writing
anything and fails the whole archive if one entry resolves outside the
target. The plugin installer now rejects such an entry the same way. Well
formed archives extract exactly as before.
2026-09-28 05:59:41 +08:00
Kenneth Rapleeandyw4z 4964f49765 Fix size_t/%d format mismatch in MsgDialog::add_button (#15852)
Fix size_t/%d mismatch in MsgDialog button keys

`m_buttons.size()` is a `size_t`, which does not match the `%d` conversion in
printf-style variadics. Build the key with `std::to_string` instead; exact
for any size_t, no behavior change for realistic counts.

Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-09-27 19:21:03 +03:00
Chris Bennight ea471dc82d Fix model initialization without a wx application (#15866)
Guard the smooth-normals preference read when CLI thumbnail generation initializes geometry without a wx application. Use the existing flat-normal path in that case and preserve GUI preferences.
2026-09-27 18:53:11 +03:00
Kiss Lorand bd65cce3f4 Unify GUI scroll rates (#15902)
Use DPI-aware 20 DIP vertical scrolling for general-purpose GUI
scroll areas.

Keep list-based views aligned to their item height so one wheel increment follows the visible row rhythm.

This makes Preferences and other dialogs scroll consistently across
Windows DPI settings.
2026-09-27 16:58:10 +03:00
51 changed files with 440 additions and 130 deletions
+3
View File
@@ -260,6 +260,9 @@ extern bool is_json_file(const std::string& path);
// Both '/' and '\\' are treated as separators on every platform, so an archive rejected on one OS
// is rejected on all of them.
extern bool is_path_within_root(const std::string &rel_path, const boost::filesystem::path &root);
// True if a symlink stored at link_rel_path (relative to root) with this target stays inside root: the target
// must be relative, and joined to the link's directory it must pass is_path_within_root.
extern bool is_symlink_target_within_root(const std::string &link_rel_path, const std::string &target, const boost::filesystem::path &root);
// Orca: custom protocal support utils
inline bool is_orca_open(const std::string& url) { return boost::starts_with(url, "orcaslicer://open"); }
+63
View File
@@ -4,6 +4,9 @@
#include "miniz_extension.hpp"
#include "Utils.hpp"
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#if defined(_MSC_VER) || defined(__MINGW64__)
#include "boost/nowide/cstdio.hpp"
#endif
@@ -115,6 +118,66 @@ std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_
return decode_zip_unicode_path_extra_field(extra.substr(0, extra_size > 0 ? extra_size - 1 : 0), stat.m_filename);
}
bool extract_archive_confined(const std::string &zip_path_utf8, const std::string &dest_dir)
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
if (!open_zip_reader(&archive, zip_path_utf8)) {
BOOST_LOG_TRIVIAL(error) << "Unable to open zip reader for " << zip_path_utf8;
return false;
}
const mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
mz_zip_archive_file_stat stat;
// Validate every entry first so an archive with a single escaping entry leaves no partial output behind.
const boost::filesystem::path root(dest_dir);
for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(&archive, i, &stat) && !is_path_within_root(stat.m_filename, root)) {
BOOST_LOG_TRIVIAL(error) << "Unzip: rejecting " << zip_path_utf8 << ", entry " << stat.m_filename << " resolves outside " << dest_dir;
close_zip_reader(&archive);
return false;
}
}
for (mz_uint i = 0; i < num_entries; ++i) {
if (!mz_zip_reader_file_stat(&archive, i, &stat)) {
BOOST_LOG_TRIVIAL(warning) << "Unzip: read file stat failed";
continue;
}
const std::string dest_file = dest_dir + "/" + stat.m_filename;
try {
if (stat.m_is_directory) {
const boost::filesystem::path dest_path(dest_file);
if (!boost::filesystem::exists(dest_path))
boost::filesystem::create_directories(dest_path);
continue;
}
if (stat.m_uncomp_size == 0) {
BOOST_LOG_TRIVIAL(warning) << "Unzip: invalid size for file " << stat.m_filename;
continue;
}
// Replace a symlink at the destination rather than writing through it.
const boost::filesystem::path dest_path(dest_file);
if (boost::filesystem::is_symlink(boost::filesystem::symlink_status(dest_path)))
boost::filesystem::remove(dest_path);
if (!mz_zip_reader_extract_to_file(&archive, stat.m_file_index, dest_file.c_str(), 0)) {
BOOST_LOG_TRIVIAL(error) << "Unzip: extract file " << stat.m_filename << " to dest " << dest_file << " failed";
close_zip_reader(&archive);
return false;
}
BOOST_LOG_TRIVIAL(info) << "Unzip: successfully extract file " << stat.m_file_index << " to " << dest_file;
} catch (const std::exception &e) {
close_zip_reader(&archive);
BOOST_LOG_TRIVIAL(error) << "Unzip: archive read exception: " << e.what();
return false;
}
}
close_zip_reader(&archive);
return true;
}
MZ_Archive::MZ_Archive()
{
mz_zip_zero_struct(&arch);
+2
View File
@@ -11,6 +11,8 @@ bool open_zip_writer(mz_zip_archive *zip, const std::string &fname_utf8);
bool close_zip_reader(mz_zip_archive *zip);
bool close_zip_writer(mz_zip_archive *zip);
std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat);
// Extracts every entry of the archive under dest_dir. Nothing is written if any entry would resolve outside dest_dir.
bool extract_archive_confined(const std::string &zip_path_utf8, const std::string &dest_dir);
class MZ_Archive {
public:
+13 -1
View File
@@ -1103,7 +1103,10 @@ bool is_path_within_root(const std::string &rel_path, const boost::filesystem::p
}
// Resolve against the canonical root so a symlink inside it cannot lead back out.
try {
const std::string root_str = boost::filesystem::weakly_canonical(root).string();
std::string root_str = boost::filesystem::weakly_canonical(root).string();
// A trailing separator on root would otherwise fail the prefix match below for every path.
while (!root_str.empty() && (root_str.back() == '/' || root_str.back() == boost::filesystem::path::preferred_separator))
root_str.pop_back();
const std::string full_str = boost::filesystem::weakly_canonical(root / rel_path).string();
return full_str.compare(0, root_str.size(), root_str) == 0 &&
(full_str.size() == root_str.size() || full_str[root_str.size()] == boost::filesystem::path::preferred_separator);
@@ -1112,6 +1115,15 @@ bool is_path_within_root(const std::string &rel_path, const boost::filesystem::p
}
}
bool is_symlink_target_within_root(const std::string &link_rel_path, const std::string &target, const boost::filesystem::path &root)
{
if (target.empty() || target.front() == '/' || target.front() == '\\' || (target.size() > 1 && target[1] == ':'))
return false;
// A relative target without ".." only descends from the link's directory, so no chain of such links can leave root.
const size_t sep = link_rel_path.find_last_of("/\\");
return is_path_within_root((sep == std::string::npos ? std::string() : link_rel_path.substr(0, sep + 1)) + target, root);
}
bool is_img_file(const std::string &path)
{
return boost::iends_with(path, ".png") || boost::iends_with(path, ".svg");
+4 -4
View File
@@ -819,7 +819,7 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) :
m_scrolled_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxHSCROLL);
m_scrolled_window->SetBackgroundColour(*wxWHITE);
m_scrolled_window->SetScrollRate(0, FromDIP(10));
m_scrolled_window->SetScrollRate(0, FromDIP(20));
wxBoxSizer *title_sizer_h= new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *title_sizer_v = new wxBoxSizer(wxVERTICAL);
@@ -1000,13 +1000,13 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) :
show_pos.x = screen_size.GetLeft();
m_scrolled_window->SetMaxSize(wxSize(screen_size.GetWidth(), popup_height));
m_scrolled_window->SetMinSize(wxSize(screen_size.GetWidth(), popup_height));
m_scrolled_window->SetScrollRate(FromDIP(10), FromDIP(10));
m_scrolled_window->SetScrollRate(FromDIP(10), FromDIP(20));
}
else
{
m_scrolled_window->SetMaxSize(wxSize(popup_width, popup_height));
m_scrolled_window->SetMinSize(wxSize(popup_width, popup_height));
m_scrolled_window->SetScrollRate(0, FromDIP(10));
m_scrolled_window->SetScrollRate(0, FromDIP(20));
}
//Vertical Direction Processing
@@ -2117,7 +2117,7 @@ void AmsReplaceMaterialDialog::create()
identical_filament->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#009688"))); // Orca: accent teal (not brand green)
m_scrollview_groups = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL);
m_scrollview_groups->SetScrollRate(5, 5);
m_scrollview_groups->SetScrollRate(5, FromDIP(20));
//m_scrollview_groups->SetMinSize(wxSize(400, 400));
//m_scrollview_groups->SetMaxSize(wxSize(400, 400));
m_scrollview_sizer = new wxBoxSizer(wxVERTICAL);
+1 -1
View File
@@ -606,7 +606,7 @@ AuFolderPanel::AuFolderPanel(wxWindow *parent, AuxiliaryFolderType type, wxWindo
wxBoxSizer *sizer_main = new wxBoxSizer(wxVERTICAL);
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL);
m_scrolledWindow->SetScrollRate(5, 5);
m_scrolledWindow->SetScrollRate(5, FromDIP(20));
wxBoxSizer *sizer_body = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *sizer_top = new wxBoxSizer(wxHORIZONTAL);
+1 -1
View File
@@ -558,7 +558,7 @@ PingCodeBindDialog::~PingCodeBindDialog() {
//show bind failed info
m_sw_bind_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(450), FromDIP(300)), wxVSCROLL);
m_sw_bind_failed_info->SetBackgroundColour(*wxWHITE);
m_sw_bind_failed_info->SetScrollRate(5, 5);
m_sw_bind_failed_info->SetScrollRate(5, FromDIP(20));
m_sw_bind_failed_info->SetMinSize(wxSize(FromDIP(450), FromDIP(90)));
m_sw_bind_failed_info->SetMaxSize(wxSize(FromDIP(450), FromDIP(90)));
+1 -1
View File
@@ -3521,7 +3521,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
cards->Show(m_box_mate, false, true);
m_form->FitInside();
m_form->SetScrollRate(0, 10); // vertical only, like Prepare's sidebar: never scroll labels out
m_form->SetScrollRate(0, FromDIP(20)); // vertical only, like Prepare's sidebar: never scroll labels out
m_form->SetMinSize(wxSize(264, -1));
// Right column: a small view toolbar over the live 3D viewport that mirrors
+1 -1
View File
@@ -115,7 +115,7 @@ HistoryWindow::HistoryWindow(wxWindow* parent, const std::vector<PACalibResult>&
auto main_sizer = new wxBoxSizer(wxVERTICAL);
auto scroll_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL);
scroll_window->SetScrollRate(5, 5);
scroll_window->SetScrollRate(5, FromDIP(20));
scroll_window->SetBackgroundColour(*wxWHITE);
scroll_window->SetMinSize(HISTORY_WINDOW_SIZE);
scroll_window->SetSize(HISTORY_WINDOW_SIZE);
+1 -1
View File
@@ -207,7 +207,7 @@ SelectMObjectPopup::SelectMObjectPopup(wxWindow* parent)
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, SELECT_MACHINE_LIST_SIZE, wxHSCROLL | wxVSCROLL);
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
m_scrolledWindow->SetMinSize(SELECT_MACHINE_LIST_SIZE);
m_scrolledWindow->SetScrollRate(0, 5);
m_scrolledWindow->SetScrollRate(0, SELECT_MACHINE_ITEM_SIZE.y);
auto m_sizxer_scrolledWindow = new wxBoxSizer(wxVERTICAL);
m_scrolledWindow->SetSizer(m_sizxer_scrolledWindow);
m_scrolledWindow->Layout();
+1 -1
View File
@@ -81,7 +81,7 @@ CalibrationWizard::CalibrationWizard(wxWindow* parent, CalibMode mode, wxWindowI
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL);
m_scrolledWindow->SetScrollRate(5, 5);
m_scrolledWindow->SetScrollRate(5, FromDIP(20));
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
wxBoxSizer* padding_sizer = new wxBoxSizer(wxHORIZONTAL);
+1 -1
View File
@@ -834,7 +834,7 @@ void CaliPageSendingPanel::create(wxWindow* parent)
m_sw_print_failed_info = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(380), FromDIP(125)), wxVSCROLL);
m_sw_print_failed_info->SetBackgroundColour(*wxWHITE);
m_sw_print_failed_info->SetScrollRate(0, 5);
m_sw_print_failed_info->SetScrollRate(0, FromDIP(20));
m_sw_print_failed_info->SetMinSize(wxSize(FromDIP(380), FromDIP(125)));
m_sw_print_failed_info->SetMaxSize(wxSize(FromDIP(380), FromDIP(125)));
+1 -1
View File
@@ -2710,7 +2710,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
SetSizerAndFit(vsizer);
// We can now enable scrolling on hscroll
p->hscroll->SetScrollRate(30, 30);
p->hscroll->SetScrollRate(30, FromDIP(20));
on_window_geometry(this, [this]() {
p->init_dialog_size();
+5 -5
View File
@@ -699,7 +699,7 @@ CreateFilamentPresetDialog::CreateFilamentPresetDialog(wxWindow *parent)
m_scrolled_preset_panel = new wxScrolledWindow(this, wxID_ANY);
m_scrolled_preset_panel->SetMaxSize(wxSize(-1, FromDIP(350)));
m_scrolled_preset_panel->SetBackgroundColour(*wxWHITE);
m_scrolled_preset_panel->SetScrollRate(5, 5);
m_scrolled_preset_panel->SetScrollRate(5, FromDIP(20));
m_scrolled_sizer = new wxBoxSizer(wxVERTICAL);
m_scrolled_sizer->Add(create_item(FilamentOptionType::PRESET_FOR_PRINTER), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5));
m_scrolled_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
@@ -1593,7 +1593,7 @@ CreatePrinterPresetDialog::CreatePrinterPresetDialog(wxWindow *parent)
m_page1 = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_page1->SetBackgroundColour(*wxWHITE);
m_page1->SetScrollRate(5, 5);
m_page1->SetScrollRate(5, FromDIP(20));
m_page2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_page2->SetBackgroundColour(*wxWHITE);
create_printer_page1(m_page1);
@@ -2652,7 +2652,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_presets_template_item(wxWindow *pa
wxBoxSizer *vertical_sizer = new wxBoxSizer(wxVERTICAL);
m_scrolled_preset_window = new wxScrolledWindow(parent);
m_scrolled_preset_window->SetScrollRate(5, 5);
m_scrolled_preset_window->SetScrollRate(5, FromDIP(20));
m_scrolled_preset_window->SetBackgroundColour(*wxWHITE);
//m_scrolled_preset_window->SetMinSize(wxSize(FromDIP(1500), FromDIP(-1)));
m_scrolled_preset_window->SetMaxSize(wxSize(FromDIP(1500), FromDIP(-1)));
@@ -4297,7 +4297,7 @@ wxBoxSizer *ExportConfigsDialog::create_select_printer(wxWindow *parent)
optionSizer->SetMinSize(OPTION_SIZE);
horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL, FromDIP(10));
m_scrolled_preset_window = new wxScrolledWindow(parent);
m_scrolled_preset_window->SetScrollRate(5, 5);
m_scrolled_preset_window->SetScrollRate(5, FromDIP(20));
m_scrolled_preset_window->SetBackgroundColour(*wxWHITE);
m_scrolled_preset_window->SetMaxSize(wxSize(FromDIP(660), FromDIP(400)));
m_scrolled_preset_window->SetSize(wxSize(FromDIP(660), FromDIP(400)));
@@ -4736,7 +4736,7 @@ wxBoxSizer *EditFilamentPresetDialog::create_preset_tree_sizer()
{
wxBoxSizer *filament_preset_tree_sizer = new wxBoxSizer(wxHORIZONTAL);
m_preset_tree_window = new wxScrolledWindow(this);
m_preset_tree_window->SetScrollRate(5, 5);
m_preset_tree_window->SetScrollRate(5, FromDIP(20));
m_preset_tree_window->SetBackgroundColour(PRINTER_LIST_COLOUR);
m_preset_tree_window->SetMinSize(wxSize(-1, FromDIP(400)));
m_preset_tree_window->SetMaxSize(wxSize(-1, FromDIP(300)));
+1 -1
View File
@@ -39,7 +39,7 @@ DeviceErrorDialog::DeviceErrorDialog(MachineObject* obj, wxWindow* parent, wxWin
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
m_scroll_area = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_scroll_area->SetScrollRate(0, 5);
m_scroll_area->SetScrollRate(0, FromDIP(20));
m_scroll_area->SetBackgroundColour(*wxWHITE);
m_scroll_area->SetMinSize(wxSize(FromDIP(320), FromDIP(250)));
+3 -2
View File
@@ -456,8 +456,9 @@ void GLModel::init_from(const indexed_triangle_set& its)
data.reserve_vertices(3 * its.indices.size());
data.reserve_indices(3 * its.indices.size());
// Read user preference: smooth normals enabled
const bool smooth_normals_enabled = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS);
// CLI thumbnail generation can initialize models without a wx application.
const bool smooth_normals_enabled = wxApp::GetInstance() != nullptr && wxGetApp().app_config != nullptr &&
wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS);
if (smooth_normals_enabled) {
// Use per-corner smooth normals (via IGL)
+26 -5
View File
@@ -1512,11 +1512,33 @@ int GUI_App::install_plugin(std::string name, std::string package_name, InstallP
size_t n = mz_zip_reader_get_extra(&archive, stat.m_file_index, extra.data(), extra.size());
dest_file = decode(extra.substr(0, n), stat.m_filename);
}
if (!is_path_within_root(dest_file, plugin_folder)) {
BOOST_LOG_TRIVIAL(error) << "[install_plugin] entry " << dest_file << " resolves outside " << plugin_folder.string();
close_zip_reader(&archive);
if (pro_fn) { pro_fn(InstallStatusUnzipFailed, 0, cancel); }
return InstallStatusUnzipFailed;
}
auto dest_path = plugin_folder / dest_file;
boost::filesystem::create_directories(dest_path.parent_path());
std::string dest_zip_file = encode_path(dest_path.string().c_str());
#ifndef WIN32
// Validate a symlink's target before anything at the destination is replaced.
const bool is_link = S_ISLNK(stat.m_external_attr >> 16);
std::string link;
if (is_link) {
link.assign(stat.m_uncomp_size, 0);
if (!mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, link.data(), stat.m_uncomp_size, 0) ||
!is_symlink_target_within_root(dest_file, link, plugin_folder)) {
BOOST_LOG_TRIVIAL(error) << "[install_plugin] link " << dest_file << " -> " << link << " is unreadable or resolves outside " << plugin_folder.string();
close_zip_reader(&archive);
if (pro_fn) { pro_fn(InstallStatusUnzipFailed, 0, cancel); }
return InstallStatusUnzipFailed;
}
}
#endif
try {
if (fs::exists(dest_path)) {
boost::filesystem::create_directories(dest_path.parent_path());
// symlink_status so that an existing symlink, dangling or not, is replaced rather than written through.
if (fs::exists(fs::symlink_status(dest_path))) {
boost::system::error_code ec;
fs::remove(dest_path, ec);
if (ec) {
@@ -1544,9 +1566,8 @@ int GUI_App::install_plugin(std::string name, std::string package_name, InstallP
}
mz_bool res = 0;
#ifndef WIN32
if (S_ISLNK(stat.m_external_attr >> 16)) {
std::string link(stat.m_uncomp_size + 1, 0);
res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, link.data(), stat.m_uncomp_size, 0);
if (is_link) {
res = 1;
try {
boost::filesystem::create_symlink(link, dest_path);
} catch (const std::exception &e) {
+2 -2
View File
@@ -2764,12 +2764,12 @@ ObjectTablePanel::ObjectTablePanel( wxWindow* parent, wxWindowID id, const wxPoi
//m_object_grid->AssignTable(m_object_grid_table);
m_side_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(440),FromDIP(480)), wxVSCROLL);
m_side_window->SetScrollRate( 0, 5 );
m_side_window->SetScrollRate(0, FromDIP(20));
m_page_sizer = new wxBoxSizer(wxVERTICAL);
//m_page_top_sizer = new wxBoxSizer(wxHORIZONTAL);
m_side_window->SetBackgroundColour(wxColour(0xff, 0xff, 0xff));
m_side_window->SetSizer(m_page_sizer);
m_side_window->SetScrollbars(1, 20, 1, 2);
m_side_window->SetScrollbars(1, FromDIP(20), 1, 2);
//m_side_window->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
//m_side_window->EnableScrolling(false, true);
+1 -1
View File
@@ -171,7 +171,7 @@ HMSPanel::HMSPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wx
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
m_scrolledWindow->SetScrollRate(5, 5);
m_scrolledWindow->SetScrollRate(5, FromDIP(20));
m_top_sizer = new wxBoxSizer(wxVERTICAL);
+1 -1
View File
@@ -211,7 +211,7 @@ wxPanel* KBShortcutsDialog::create_page(wxWindow* parent, const Page& page)
wxGetApp().UpdateDarkUI(scrollable_panel);
const wxColour page_colour = StateColor::darkModeColorFor(*wxWHITE);
scrollable_panel->SetBackgroundColour(page_colour);
scrollable_panel->SetScrollRate(0, 20);
scrollable_panel->SetScrollRate(0, FromDIP(20));
const int page_width = FromDIP(PAGE_WIDTH);
scrollable_panel->SetInitialSize(wxSize(page_width, FromDIP(450)));
+1 -1
View File
@@ -1125,7 +1125,7 @@ wxBoxSizer* MixedFilamentDialog::create_recommendation_grid()
outer->Add(title_sizer, 0, wxEXPAND | wxBOTTOM, FromDIP(4));
m_recommendation_scroll = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(116)));
m_recommendation_scroll->SetScrollRate(0, 5);
m_recommendation_scroll->SetScrollRate(0, FromDIP(20));
m_recommendation_scroll->SetBackgroundColour(StateColor::darkModeColorFor(wxColour("#F8F8F8")));
m_recommendation_grid = new wxWrapSizer(wxHORIZONTAL, wxREMOVE_LEADING_SPACES);
+4 -3
View File
@@ -194,9 +194,10 @@ Button* MsgDialog::add_button(wxWindowID btn_id, bool set_focus /*= false*/, con
bd->button = btn;
//bd->type = type;
mb->id = wxString::Format("%d", m_buttons.size());
// m_buttons.size() is a size_t, which does not match the %d conversion
mb->id = wxString(std::to_string(m_buttons.size()));
mb->buttondata = bd;
m_buttons[ wxString::Format("%d", m_buttons.size())] = mb;
m_buttons[ wxString(std::to_string(m_buttons.size()))] = mb;
return btn;
};
@@ -398,7 +399,7 @@ static void add_msg_content(wxWindow *parent,
}
wxScrolledWindow *scrolledWindow = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
scrolledWindow->SetBackgroundColour(*wxWHITE);
scrolledWindow->SetScrollRate(0, 20);
scrolledWindow->SetScrollRate(0, scrolledWindow->FromDIP(20));
scrolledWindow->EnableScrolling(false, true);
wxBoxSizer *sizer_scrolled = new wxBoxSizer(wxHORIZONTAL);
Label *wrapped_text = new Label(scrolledWindow, font, msg, LB_AUTO_WRAP, wxSize(info_width, -1));
+1 -1
View File
@@ -398,7 +398,7 @@ MultiMachineManagerPage::MultiMachineManagerPage(wxWindow* parent)
m_machine_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_machine_list->SetBackgroundColour(*wxWHITE);
m_machine_list->SetScrollRate(0, 5);
m_machine_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT));
m_machine_list->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
m_machine_list->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
+1 -1
View File
@@ -323,7 +323,7 @@ MultiMachinePickPage::MultiMachinePickPage(Plater* plater /*= nullptr*/)
scroll_macine_list->SetMinSize(wxSize(FromDIP(400), FromDIP(10 * 30)));
scroll_macine_list->SetMaxSize(wxSize(FromDIP(400), FromDIP(10 * 30)));
scroll_macine_list->SetBackgroundColour(*wxWHITE);
scroll_macine_list->SetScrollRate(0, 5);
scroll_macine_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT));
sizer_machine_list = new wxBoxSizer(wxVERTICAL);
scroll_macine_list->SetSizer(sizer_machine_list);
+2 -2
View File
@@ -675,7 +675,7 @@ LocalTaskManagerPage::LocalTaskManagerPage(wxWindow* parent)
m_task_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_task_list->SetBackgroundColour(*wxWHITE);
m_task_list->SetScrollRate(0, 5);
m_task_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT));
m_task_list->SetMinSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
m_task_list->SetMaxSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
@@ -1056,7 +1056,7 @@ CloudTaskManagerPage::CloudTaskManagerPage(wxWindow* parent)
m_task_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_task_list->SetBackgroundColour(*wxWHITE);
m_task_list->SetScrollRate(0, 5);
m_task_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT));
m_task_list->SetMinSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
m_task_list->SetMaxSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
+1 -1
View File
@@ -355,7 +355,7 @@ ObjColorPanel::ObjColorPanel(wxWindow *parent, Slic3r::ObjDialogInOut &in_out, c
//new color table
m_scrolledWindow = new wxScrolledWindow(m_page_simple, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
m_scrolledWindow->SetScrollRate(0, 20);
m_scrolledWindow->SetScrollRate(0, FromDIP(20));
m_scrolledWindow->EnableScrolling(false, true);
m_scrolledWindow->ShowScrollbars(wxScrollbarVisibility::wxSHOW_SB_NEVER, wxScrollbarVisibility::wxSHOW_SB_DEFAULT);
draw_new_table();
+1 -1
View File
@@ -391,7 +391,7 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c
m_page_sizer = new wxBoxSizer(wxVERTICAL);
m_page_view->SetSizer(m_page_sizer);
m_page_view->SetScrollbars(1, 20, 1, 2);
m_page_view->SetScrollbars(1, FromDIP(20), 1, 2);
//m_page_view->SetScrollRate( 5, 5 );
if (m_mode_region)
+1 -1
View File
@@ -148,7 +148,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _
m_line->SetBackgroundColour(wxColor(238, 238, 238));
m_list_view = new wxScrolledWindow(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(267, -1), wxHSCROLL | wxVSCROLL);
m_list_view->SetScrollRate(5, 5);
m_list_view->SetScrollRate(5, FromDIP(30));
m_list_view->SetMinSize(wxSize(FromDIP(267), FromDIP(378)));
m_list_view->SetMaxSize(wxSize(FromDIP(267), FromDIP(378)));
m_list_view->SetBackgroundColour(*wxWHITE);
+2 -4
View File
@@ -3049,8 +3049,7 @@ Sidebar::Sidebar(Plater *parent)
// add filament content
p->m_panel_filament_content = new wxScrolledWindow(p->m_filament_area_wrapper, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
p->m_panel_filament_content->SetScrollbars(0, 100, 1, 2);
p->m_panel_filament_content->SetScrollRate(0, 5);
p->m_panel_filament_content->SetScrollRate(0, FromDIP(20));
//p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(174)});
p->m_panel_filament_content->SetBackgroundColour(wxColour(255, 255, 255));
@@ -3142,8 +3141,7 @@ Sidebar::Sidebar(Plater *parent)
// 3) Mixed filament rows, in their own scroll area so a long mixed list does not
// push the physical filament list off screen.
p->m_mixed_scroll_area = new wxScrolledWindow(p->m_filament_area_wrapper, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
p->m_mixed_scroll_area->SetScrollbars(0, 100, 1, 2);
p->m_mixed_scroll_area->SetScrollRate(0, 5);
p->m_mixed_scroll_area->SetScrollRate(0, FromDIP(20));
p->m_mixed_scroll_area->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
{
auto* mix_scroll_sizer = new wxBoxSizer(wxVERTICAL);
+1 -1
View File
@@ -1475,7 +1475,7 @@ void PreferencesDialog::create()
app_config = get_app_config();
m_parent = new MyscrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_parent->SetScrollRate(5, 5);
m_parent->SetScrollRate(0, FromDIP(20));
m_parent->SetBackgroundColour(*wxWHITE);
m_sizer_body = new wxBoxSizer(wxVERTICAL);
+1 -1
View File
@@ -32,7 +32,7 @@ PrintOptionsDialog::PrintOptionsDialog(wxWindow* parent)
m_scrollwindow = new wxScrolledWindow(this, wxID_ANY);
m_scrollwindow->SetScrollRate(0, FromDIP(10));
m_scrollwindow->SetScrollRate(0, FromDIP(20));
m_scrollwindow->SetBackgroundColour(*wxWHITE);
m_scrollwindow->SetMinSize(wxSize(FromDIP(480), wxDefaultCoord));
m_scrollwindow->SetMaxSize(wxSize(FromDIP(480), wxDefaultCoord));
+1 -1
View File
@@ -1225,7 +1225,7 @@ size_t PublishSettingsDialog::category_index_for(
}
category.scroll = new wxScrolledWindow(category.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
category.scroll->SetScrollRate(0, 10);
category.scroll->SetScrollRate(0, FromDIP(20));
category.scroll->SetBackgroundColour(GetBackgroundColour());
category.list_sizer = new wxBoxSizer(wxVERTICAL);
category.scroll->SetSizer(category.list_sizer);
+6 -6
View File
@@ -76,7 +76,7 @@ ReleaseNoteDialog::ReleaseNoteDialog(Plater *plater /*= nullptr*/)
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15));
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL);
m_vebview_release_note->SetScrollRate(5, 5);
m_vebview_release_note->SetScrollRate(5, FromDIP(20));
m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
m_vebview_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430)));
@@ -142,7 +142,7 @@ UpdatePluginDialog::UpdatePluginDialog(wxWindow* parent /*= nullptr*/)
operation_tips->SetMaxSize(wxSize(FromDIP(260), -1));
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_vebview_release_note->SetScrollRate(5, 5);
m_vebview_release_note->SetScrollRate(5, FromDIP(20));
m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
m_vebview_release_note->SetMinSize(wxSize(FromDIP(260), FromDIP(150)));
m_vebview_release_note->SetMaxSize(wxSize(FromDIP(260), FromDIP(150)));
@@ -277,7 +277,7 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent)
m_simplebook_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
m_scrollwindows_release_note = new wxScrolledWindow(m_simplebook_release_note, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL);
m_scrollwindows_release_note->SetScrollRate(5, 5);
m_scrollwindows_release_note->SetScrollRate(5, FromDIP(20));
m_scrollwindows_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
//webview
@@ -538,7 +538,7 @@ SecondaryCheckDialog::SecondaryCheckDialog(wxWindow* parent, wxWindowID id, cons
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15));
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_vebview_release_note->SetScrollRate(0, 5);
m_vebview_release_note->SetScrollRate(0, FromDIP(20));
m_vebview_release_note->SetBackgroundColour(*wxWHITE);
m_vebview_release_note->SetMinSize(wxSize(FromDIP(400), FromDIP(380)));
m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15));
@@ -823,7 +823,7 @@ PrintErrorDialog::PrintErrorDialog(wxWindow* parent, wxWindowID id, const wxStri
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(5));
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_vebview_release_note->SetScrollRate(0, 5);
m_vebview_release_note->SetScrollRate(0, FromDIP(20));
m_vebview_release_note->SetBackgroundColour(*wxWHITE);
m_vebview_release_note->SetMinSize(wxSize(FromDIP(320), FromDIP(250)));
m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15));
@@ -1122,7 +1122,7 @@ ConfirmBeforeSendDialog::ConfirmBeforeSendDialog(wxWindow* parent, wxWindowID id
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15));
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_vebview_release_note->SetScrollRate(0, 5);
m_vebview_release_note->SetScrollRate(0, FromDIP(20));
m_vebview_release_note->SetBackgroundColour(*wxWHITE);
m_vebview_release_note->SetMinSize(wxSize(FromDIP(400), FromDIP(380)));
m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15));
+2 -2
View File
@@ -31,7 +31,7 @@ SafetyOptionsDialog::SafetyOptionsDialog(wxWindow* parent)
SetSize(FromDIP(480),FromDIP(320));
m_scrollwindow = new wxScrolledWindow(this, wxID_ANY);
m_scrollwindow->SetScrollRate(0, FromDIP(10));
m_scrollwindow->SetScrollRate(0, FromDIP(20));
m_scrollwindow->SetBackgroundColour(*wxWHITE);
m_scrollwindow->SetMinSize(wxSize(FromDIP(480), wxDefaultCoord));
m_scrollwindow->SetMaxSize(wxSize(FromDIP(480), wxDefaultCoord));
@@ -272,4 +272,4 @@ void SafetyOptionsDialog::show_idel_heating_toast(const wxString &text)
m_idel_heating_toast_timer.StartOnce(3000);
}
}} // namespace Slic3r::GUI
}} // namespace Slic3r::GUI
+2 -2
View File
@@ -148,7 +148,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
wxBoxSizer* m_scroll_sizer = new wxBoxSizer(wxVERTICAL);
m_scroll_area = new wxScrolledWindow(this);
m_scroll_area->SetScrollRate(0, 20);
m_scroll_area->SetScrollRate(0, FromDIP(20));
m_scroll_area->SetBackgroundColour(m_colour_def_color);
m_scroll_area->SetMinSize(wxSize(FromDIP(700), FromDIP(600)));
m_scroll_area->SetMaxSize(wxSize(FromDIP(700), FromDIP(600)));
@@ -707,7 +707,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
//show bind failed info
m_sw_print_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125)), wxVSCROLL);
m_sw_print_failed_info->SetBackgroundColour(*wxWHITE);
m_sw_print_failed_info->SetScrollRate(0, 5);
m_sw_print_failed_info->SetScrollRate(0, FromDIP(20));
m_sw_print_failed_info->SetMinSize(wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125)));
m_sw_print_failed_info->SetMaxSize(wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125)));
+1 -1
View File
@@ -327,7 +327,7 @@ SelectMachinePopup::SelectMachinePopup(wxWindow *parent)
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, SELECT_MACHINE_LIST_SIZE, wxHSCROLL | wxVSCROLL);
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
m_scrolledWindow->SetMinSize(SELECT_MACHINE_LIST_SIZE);
m_scrolledWindow->SetScrollRate(0, 5);
m_scrolledWindow->SetScrollRate(0, SELECT_MACHINE_ITEM_SIZE.y);
auto m_sizxer_scrolledWindow = new wxBoxSizer(wxVERTICAL);
m_scrolledWindow->SetSizer(m_sizxer_scrolledWindow);
m_scrolledWindow->Layout();
+2 -2
View File
@@ -278,7 +278,7 @@ SendMultiMachinePage::SendMultiMachinePage(Plater* plater)
m_main_scroll = new ScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_main_scroll->SetBackgroundColour(*wxWHITE);
m_main_scroll->SetScrollRate(5, 5);
m_main_scroll->SetScrollRate(5, FromDIP(20));
m_sizer_body = new wxBoxSizer(wxVERTICAL);
m_main_page = create_page();
@@ -1353,7 +1353,7 @@ wxPanel* SendMultiMachinePage::create_page()
scroll_macine_list = new wxScrolledWindow(main_page, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(800), FromDIP(300)), wxHSCROLL | wxVSCROLL);
scroll_macine_list->SetBackgroundColour(*wxWHITE);
scroll_macine_list->SetScrollRate(5, 5);
scroll_macine_list->SetScrollRate(5, FromDIP(SEND_ITEM_MAX_HEIGHT));
scroll_macine_list->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(SEND_ITEM_MAX_HEIGHT)));
scroll_macine_list->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(SEND_ITEM_MAX_HEIGHT)));
+1 -1
View File
@@ -384,7 +384,7 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
//show bind failed info
m_sw_print_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(380), FromDIP(125)), wxVSCROLL);
m_sw_print_failed_info->SetBackgroundColour(*wxWHITE);
m_sw_print_failed_info->SetScrollRate(0, 5);
m_sw_print_failed_info->SetScrollRate(0, FromDIP(20));
m_sw_print_failed_info->SetMinSize(wxSize(FromDIP(380), FromDIP(125)));
m_sw_print_failed_info->SetMaxSize(wxSize(FromDIP(380), FromDIP(125)));
+1 -1
View File
@@ -1302,7 +1302,7 @@ void PrintingTaskPanel::set_star_count(int star_count)
StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name)
: wxScrolledWindow(parent, id, pos, size, wxHSCROLL | wxVSCROLL)
{
this->SetScrollRate(25, 25);
SetScrollRate(25, FromDIP(20));
Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return;
obj = dev->get_selected_machine();
+1 -1
View File
@@ -700,7 +700,7 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
//wxBoxSizer *m_scroll_sizer = new wxBoxSizer(wxVERTICAL);
m_scrolledWindow = new wxScrolledWindow(m_show_page, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
m_scrolledWindow->SetScrollRate(0, 20);
m_scrolledWindow->SetScrollRate(0, FromDIP(20));
m_scrolledWindow->SetMinSize(wxSize(-1, SyncAmsInfoDialogHeightMAX));
m_scrolledWindow->SetMaxSize(wxSize(-1, SyncAmsInfoDialogHeightMAX));
m_scrolledWindow->EnableScrolling(false,true);
+3 -3
View File
@@ -534,7 +534,7 @@ public:
m_content = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_content->SetBackgroundColour(pop_bg);
m_content->SetScrollRate(0, FromDIP(5));
m_content->SetScrollRate(0, FromDIP(20));
auto* outer = new wxBoxSizer(wxVERTICAL);
const int pop_w = std::max(FromDIP(213), popup_width);
@@ -2274,7 +2274,7 @@ void TextureImportDialog::build_mapping_panel(wxWindow* parent, wxSizer* sizer)
m_mapping_scroll = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition,
wxSize(-1, FromDIP(300)));
m_mapping_scroll->SetScrollRate(0, FromDIP(10));
m_mapping_scroll->SetScrollRate(0, FromDIP(20));
m_mapping_scroll->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
m_mapping_scroll->Bind(wxEVT_MOUSEWHEEL, &TextureImportDialog::dismiss_filament_popup_on_wheel, this);
@@ -4335,7 +4335,7 @@ void TextureImportDialog::on_dpi_changed(const wxRect&)
if (m_mapping_scroll) {
m_mapping_scroll->SetMinSize(wxSize(-1, FromDIP(300)));
m_mapping_scroll->SetScrollRate(0, FromDIP(10));
m_mapping_scroll->SetScrollRate(0, FromDIP(20));
}
if (m_btn_skip) {
+1 -1
View File
@@ -942,7 +942,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
m_sizer_tab->Add(m_table_top, 1, 0, 0);
m_scrolledWindow = new wxScrolledWindow(m_panel_tab, wxID_ANY, wxDefaultPosition, UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE, wxNO_BORDER|wxVSCROLL);
m_scrolledWindow->SetScrollRate(0, 5);
m_scrolledWindow->SetScrollRate(0, FromDIP(20));
m_scrolledWindow->SetBackgroundColour(GREY200);
m_sizer_bottom = new wxBoxSizer(wxVERTICAL);
m_sizer_bottom->Add(m_scrolledWindow, 1, wxEXPAND, 0);
+1 -1
View File
@@ -130,7 +130,7 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, bool force_
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15));
auto m_scrollwindw_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)),wxVSCROLL);
m_scrollwindw_release_note->SetScrollRate(0, 5);
m_scrollwindw_release_note->SetScrollRate(0, FromDIP(20));
m_scrollwindw_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
m_scrollwindw_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430)));
m_scrollwindw_release_note->SetWindowStyle(wxVSCROLL);
+1 -1
View File
@@ -1629,7 +1629,7 @@ UpgradePanel::UpgradePanel(wxWindow *parent, wxWindowID id, const wxPoint &pos,
auto m_main_sizer = new wxBoxSizer(wxVERTICAL);
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_scrolledWindow->SetScrollRate(5, 25);
m_scrolledWindow->SetScrollRate(5, FromDIP(20));
m_machine_list_sizer = new wxBoxSizer(wxVERTICAL);
+2 -2
View File
@@ -62,7 +62,7 @@ CheckList::CheckList(
s_sizer = new wxBoxSizer(wxVERTICAL);
m_scroll_area = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, scroll_style);
m_scroll_area->SetScrollRate(0, 10);
m_scroll_area->SetScrollRate(0, FromDIP(20));
m_scroll_area->SetSizer(s_sizer);
m_scroll_area->SetBackgroundColour(parent->GetBackgroundColour());
m_scroll_area->Bind(wxEVT_RIGHT_DOWN, &CheckList::ShowMenu, this);
@@ -223,4 +223,4 @@ void CheckList::ShowMenu(wxMouseEvent &evt)
wxPoint screen_pos = src->ClientToScreen(evt.GetPosition());
wxPoint local_pos = ScreenToClient(screen_pos);
PopupMenu(&m, local_pos);
}
}
+1 -1
View File
@@ -199,7 +199,7 @@ bool ProgressDialog::Create(const wxString &title, const wxString &message, int
m_sizer_main->Add(m_simplebook, 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(28));
} else {
m_msg_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL );
m_msg_scrolledWindow->SetScrollRate(0,5);
m_msg_scrolledWindow->SetScrollRate(0, FromDIP(20));
wxBoxSizer* m_msg_sizer= new wxBoxSizer(wxVERTICAL);
m_msg = new wxStaticText(m_msg_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x, -1), 0);
+1 -1
View File
@@ -1222,7 +1222,7 @@ ImageTransientPopup::ImageTransientPopup( wxWindow *parent, bool scrolled, wxBit
m_panel->SetSize(300, 300);
// And also actually enable them.
m_panel->SetScrollRate(10, 10);
m_panel->SetScrollRate(10, FromDIP(20));
}
else
{
+2 -56
View File
@@ -339,62 +339,8 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe
//BBS: refine preset update logic
bool PresetUpdater::priv::extract_file(const fs::path &source_path, const fs::path &dest_path)
{
bool res = true;
std::string file_path = source_path.string();
std::string parent_path = (!dest_path.empty() ? dest_path : source_path.parent_path()).string();
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
if (!open_zip_reader(&archive, file_path))
{
BOOST_LOG_TRIVIAL(error) << "Unable to open zip reader for "<<file_path;
return false;
}
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
mz_zip_archive_file_stat stat;
// we first loop the entries to read from the archive the .amf file only, in order to extract the version from it
for (mz_uint i = 0; i < num_entries; ++i)
{
if (mz_zip_reader_file_stat(&archive, i, &stat))
{
std::string dest_file = parent_path+"/"+stat.m_filename;
if (stat.m_is_directory) {
fs::path dest_path(dest_file);
if (!fs::exists(dest_path))
fs::create_directories(dest_path);
continue;
}
else if (stat.m_uncomp_size == 0) {
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater]Unzip: invalid size for file "<<stat.m_filename;
continue;
}
try
{
res = mz_zip_reader_extract_to_file(&archive, stat.m_file_index, dest_file.c_str(), 0);
if (!res) {
BOOST_LOG_TRIVIAL(error) << "[Orca Updater]extract file "<<stat.m_filename<<" to dest "<<dest_file<<" failed";
close_zip_reader(&archive);
return res;
}
BOOST_LOG_TRIVIAL(info) << "[Orca Updater]successfully extract file " << stat.m_file_index << " to "<<dest_file;
}
catch (const std::exception& e)
{
// ensure the zip archive is closed and rethrow the exception
close_zip_reader(&archive);
BOOST_LOG_TRIVIAL(error) << "[Orca Updater]Archive read exception:"<<e.what();
return false;
}
}
else {
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater]Unzip: read file stat failed";
}
}
close_zip_reader(&archive);
return true;
const std::string parent_path = (!dest_path.empty() ? dest_path : source_path.parent_path()).string();
return extract_archive_confined(source_path.string(), parent_path);
}
// Remove a leftover partial archive for the vendor about to be synchronized.
+1
View File
@@ -40,6 +40,7 @@ add_executable(${_TEST_NAME}_tests
test_lay_on_face.cpp
test_model.cpp
test_utils.cpp
test_miniz_extension.cpp
test_timeutils.cpp
test_voronoi.cpp
test_wipe_tower_estimate.cpp
+194
View File
@@ -0,0 +1,194 @@
#include <catch2/catch_all.hpp>
#include "libslic3r/miniz_extension.hpp"
#include "test_utils.hpp"
#include <boost/filesystem.hpp>
#include <algorithm>
#include <fstream>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
using namespace Slic3r;
namespace fs = boost::filesystem;
namespace {
void write_zip(const fs::path &zip_file, const std::vector<std::pair<std::string, std::string>> &entries)
{
mz_zip_archive zip;
mz_zip_zero_struct(&zip);
REQUIRE(open_zip_writer(&zip, zip_file.string()));
for (const auto &[name, content] : entries)
REQUIRE(mz_zip_writer_add_mem(&zip, name.c_str(), content.data(), content.size(), MZ_DEFAULT_COMPRESSION));
REQUIRE(mz_zip_writer_finalize_archive(&zip));
REQUIRE(close_zip_writer(&zip));
}
// miniz refuses to write a name starting with '/', so write a placeholder of the same length and patch it in place.
void rename_entry(const fs::path &zip_file, const std::string &from, const std::string &to)
{
REQUIRE(from.size() == to.size());
std::string bytes;
{
std::ifstream in(zip_file.string(), std::ios::binary);
bytes.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
}
size_t count = 0;
for (size_t pos = bytes.find(from); pos != std::string::npos; pos = bytes.find(from, pos + to.size()), ++count)
bytes.replace(pos, from.size(), to);
// Once in the local header and once in the central directory.
REQUIRE(count == 2);
std::ofstream out(zip_file.string(), std::ios::binary | std::ios::trunc);
out << bytes;
}
std::vector<std::string> list_dir(const fs::path &dir)
{
std::vector<std::string> names;
for (const fs::directory_entry &entry : fs::directory_iterator(dir))
names.push_back(entry.path().filename().string());
std::sort(names.begin(), names.end());
return names;
}
std::string read_file(const fs::path &file)
{
std::ifstream in(file.string(), std::ios::binary);
return std::string(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
}
} // namespace
TEST_CASE("Confined extraction writes a well-formed archive under the target directory", "[MinizExtension]")
{
ScopedTemporaryDir tmp;
const fs::path zip_file = tmp.path() / "bundle.zip";
const fs::path target = tmp.path() / "cache";
fs::create_directories(target);
write_zip(zip_file, {{"vendor/", ""}, {"vendor/machine/", ""}, {"vendor.json", "{\"a\":1}"}, {"vendor/machine/printer.json", "{\"b\":2}"}});
REQUIRE(extract_archive_confined(zip_file.string(), target.string()));
CHECK(fs::is_directory(target / "vendor"));
CHECK(read_file(target / "vendor.json") == "{\"a\":1}");
CHECK(read_file(target / "vendor" / "machine" / "printer.json") == "{\"b\":2}");
}
TEST_CASE("Confined extraction rejects an archive with an entry outside the target directory", "[MinizExtension]")
{
ScopedTemporaryDir tmp;
const fs::path zip_file = tmp.path() / "bundle.zip";
const fs::path target = tmp.path() / "cache";
fs::create_directories(target);
const std::string escaping_entry = GENERATE(std::string("../escape.txt"), std::string("..\\escape.txt"),
std::string("sub/../../escape.txt"), std::string("C:/escape.txt"),
std::string("C:escape.txt"), std::string("\\escape.txt"));
// The normal entry comes first so a per-entry check would already have written it.
write_zip(zip_file, {{"normal.json", "{}"}, {escaping_entry, "escaped"}});
CAPTURE(escaping_entry);
CHECK_FALSE(extract_archive_confined(zip_file.string(), target.string()));
CHECK_FALSE(fs::exists(tmp.path() / "escape.txt"));
CHECK(fs::is_empty(target));
}
TEST_CASE("Confined extraction rejects an archive with an absolute entry name", "[MinizExtension]")
{
ScopedTemporaryDir tmp;
const fs::path zip_file = tmp.path() / "bundle.zip";
const fs::path target = tmp.path() / "cache";
fs::create_directories(target);
const std::string absolute = (tmp.path() / "escape.txt").generic_string();
const std::string placeholder = "#" + absolute.substr(1);
write_zip(zip_file, {{"normal.json", "{}"}, {placeholder, "escaped"}});
rename_entry(zip_file, placeholder, absolute);
CHECK_FALSE(extract_archive_confined(zip_file.string(), target.string()));
CHECK_FALSE(fs::exists(tmp.path() / "escape.txt"));
CHECK(fs::is_empty(target));
}
TEST_CASE("Confined extraction rejects a directory entry outside the target directory", "[MinizExtension]")
{
ScopedTemporaryDir tmp;
const fs::path zip_file = tmp.path() / "bundle.zip";
const fs::path target = tmp.path() / "cache";
fs::create_directories(target);
write_zip(zip_file, {{"vendor/", ""}, {"../outside/", ""}});
CHECK_FALSE(extract_archive_confined(zip_file.string(), target.string()));
CHECK_FALSE(fs::exists(tmp.path() / "outside"));
CHECK(fs::is_empty(target));
}
TEST_CASE("Confined extraction validates zero-size entries like any other", "[MinizExtension]")
{
ScopedTemporaryDir tmp;
const fs::path zip_file = tmp.path() / "bundle.zip";
const fs::path target = tmp.path() / "cache";
fs::create_directories(target);
SECTION("an empty file inside the target does not fail the archive") {
write_zip(zip_file, {{"empty.json", ""}, {"vendor.json", "{}"}});
CHECK(extract_archive_confined(zip_file.string(), target.string()));
CHECK(read_file(target / "vendor.json") == "{}");
}
SECTION("an empty file outside the target rejects the archive") {
write_zip(zip_file, {{"vendor.json", "{}"}, {"../escape.txt", ""}});
CHECK_FALSE(extract_archive_confined(zip_file.string(), target.string()));
CHECK_FALSE(fs::exists(tmp.path() / "escape.txt"));
CHECK(fs::is_empty(target));
}
}
TEST_CASE("Confined extraction writes nothing outside the target for Windows-specific name forms", "[MinizExtension]")
{
ScopedTemporaryDir tmp;
const fs::path zip_file = tmp.path() / "bundle.zip";
const fs::path target = tmp.path() / "cache";
fs::create_directories(target);
// Windows strips trailing dots and spaces and maps device names; whether these extract depends on the
// platform, but none of them may land beside the target.
const std::string name = GENERATE(std::string("name."), std::string("name "), std::string("..."), std::string(".. "),
std::string(".. /escape.txt"), std::string(".../escape.txt"), std::string("CON"),
std::string("sub/NUL.txt"), std::string("C:escape.txt"));
write_zip(zip_file, {{name, "payload"}});
CAPTURE(name);
extract_archive_confined(zip_file.string(), target.string());
CHECK(list_dir(tmp.path()) == std::vector<std::string>{"bundle.zip", "cache"});
}
#ifndef _WIN32
TEST_CASE("Confined extraction replaces a symlink at the destination instead of writing through it", "[MinizExtension]")
{
ScopedTemporaryDir tmp;
const fs::path zip_file = tmp.path() / "bundle.zip";
const fs::path target = tmp.path() / "cache";
const fs::path outside = tmp.path() / "outside";
fs::create_directories(target);
fs::create_directories(outside);
write_zip(zip_file, {{"vendor.json", "{\"a\":1}"}});
SECTION("a dangling symlink") {
fs::create_symlink(outside / "vendor.json", target / "vendor.json");
CHECK(extract_archive_confined(zip_file.string(), target.string()));
CHECK_FALSE(fs::exists(outside / "vendor.json"));
CHECK_FALSE(fs::is_symlink(fs::symlink_status(target / "vendor.json")));
CHECK(read_file(target / "vendor.json") == "{\"a\":1}");
}
SECTION("a symlink to an existing file") {
{ std::ofstream((outside / "vendor.json").string()) << "original"; }
fs::create_symlink(outside / "vendor.json", target / "vendor.json");
extract_archive_confined(zip_file.string(), target.string());
CHECK(read_file(outside / "vendor.json") == "original");
}
}
#endif
+68
View File
@@ -152,3 +152,71 @@ TEST_CASE("resolve_cli_input_path leaves inputs that must not be completed uncha
REQUIRE(resolve_cli_input_path("").empty());
}
}
TEST_CASE("is_path_within_root accepts a root given with a trailing separator", "[utils]") {
ScopedTemporaryDir tmp;
const std::string root = tmp.path().string();
const std::string with_separator = GENERATE_COPY(root + "/", root + std::string(1, static_cast<char>(boost::filesystem::path::preferred_separator)));
CAPTURE(with_separator);
CHECK(is_path_within_root("vendor.json", with_separator));
CHECK(is_path_within_root("vendor/machine/printer.json", with_separator));
CHECK_FALSE(is_path_within_root("../vendor.json", with_separator));
}
TEST_CASE("is_path_within_root treats Windows-specific name forms the same on every platform", "[utils]") {
ScopedTemporaryDir tmp;
SECTION("names ending in dots or spaces stay inside the root") {
const std::string name = GENERATE(std::string("name."), std::string("name "), std::string("dir./file.json"), std::string("dir /file.json"));
CAPTURE(name);
CHECK(is_path_within_root(name, tmp.path()));
}
SECTION("drive-relative names are rejected") {
const std::string name = GENERATE(std::string("C:x"), std::string("c:x/y.json"), std::string("C:"));
CAPTURE(name);
CHECK_FALSE(is_path_within_root(name, tmp.path()));
}
}
TEST_CASE("is_symlink_target_within_root accepts relative targets that stay inside the root", "[utils]") {
ScopedTemporaryDir tmp;
const auto [link, target] = GENERATE(std::make_pair(std::string("Versions/Current"), std::string("A")),
std::make_pair(std::string("Foo.framework/Foo"), std::string("Versions/Current/Foo")),
std::make_pair(std::string("libfoo.so"), std::string("libfoo.so.1")),
std::make_pair(std::string("a/b/link"), std::string("c/d")));
CAPTURE(link, target);
CHECK(is_symlink_target_within_root(link, target, tmp.path()));
}
TEST_CASE("is_symlink_target_within_root rejects absolute targets and targets that climb out", "[utils]") {
ScopedTemporaryDir tmp;
const std::string outside = (tmp.path().parent_path() / "outside").generic_string();
const auto [link, target] = GENERATE_COPY(std::make_pair(std::string("sub/link"), outside),
std::make_pair(std::string("sub/link"), std::string("/etc/passwd")),
std::make_pair(std::string("sub/link"), std::string("\\outside")),
std::make_pair(std::string("sub/link"), std::string("C:/outside")),
std::make_pair(std::string("sub/link"), std::string("C:outside")),
std::make_pair(std::string("sub/link"), std::string("")),
std::make_pair(std::string("link"), std::string("..")),
std::make_pair(std::string("link"), std::string("../outside")),
std::make_pair(std::string("sub/link"), std::string("../../outside")),
std::make_pair(std::string("sub/link"), std::string("x/../../../outside")),
std::make_pair(std::string("sub/link"), std::string("..\\..\\outside")));
CAPTURE(link, target);
CHECK_FALSE(is_symlink_target_within_root(link, target, tmp.path()));
}
#ifndef _WIN32
TEST_CASE("is_symlink_target_within_root rejects a target that passes through a symlink leading out", "[utils]") {
ScopedTemporaryDir tmp;
const boost::filesystem::path root = tmp.path() / "root";
const boost::filesystem::path outside = tmp.path() / "outside";
boost::filesystem::create_directories(root);
boost::filesystem::create_directories(outside);
boost::filesystem::create_symlink(outside, root / "out");
CHECK_FALSE(is_symlink_target_within_root("link", "out/lib.so", root));
CHECK(is_symlink_target_within_root("link", "in/lib.so", root));
}
#endif