From 415877d79e7edf26868e71afdbc3277948dec765 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 18 Jun 2019 18:02:40 +0200
Subject: [PATCH 1/9] Experiments with msw_rescale fixing

---
 src/slic3r/GUI/GUI_ObjectManipulation.cpp | 33 +++++++++++++++------
 src/slic3r/GUI/GUI_Utils.hpp              | 36 ++++++++++++++++++++---
 src/slic3r/GUI/wxExtensions.cpp           |  2 ++
 3 files changed, 58 insertions(+), 13 deletions(-)

diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
index 5bf25f3fc..925ba65bc 100644
--- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp
+++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
@@ -130,8 +130,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
 
     auto manifold_warning_icon = [this](wxWindow* parent) {
         m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
-        auto sizer = new wxBoxSizer(wxHORIZONTAL);
-        sizer->Add(m_fix_throught_netfab_bitmap);
+//         auto sizer = new wxBoxSizer(wxHORIZONTAL);
+//         sizer->Add(m_fix_throught_netfab_bitmap);
 
         if (is_windows10())
             m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e)
@@ -144,17 +144,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
                 update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list());
             });
 
-        return sizer;
+//         return sizer;
+        return m_fix_throught_netfab_bitmap;
     };
 
-    line.append_widget(manifold_warning_icon);
+ //   line.append_widget(manifold_warning_icon);
+    line.near_label_widget = manifold_warning_icon;
     def.label = "";
     def.gui_type = "legend";
     def.tooltip = L("Object name");
 #ifdef __APPLE__
-    def.width = 19;
+    def.width = 20;
 #else
-    def.width = 21;
+    def.width = 22;
 #endif
     def.set_default_value(new ConfigOptionString{ " " });
     line.append_option(Option(def, "object_name"));
@@ -233,10 +235,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
 
     // call back for a rescale of button "Set uniform scale"
     m_og->rescale_near_label_widget = [this](wxWindow* win) {
+        // rescale lock icon
         auto *ctrl = dynamic_cast<LockButton*>(win);
-        if (ctrl == nullptr)
+        if (ctrl != nullptr) {
+            ctrl->msw_rescale();
             return;
-        ctrl->msw_rescale();
+        }
+
+        if (win == m_fix_throught_netfab_bitmap)
+            return;
+
+        // rescale "place" of the empty icon (to correct layout of the "Size" and "Scale")
+        if (dynamic_cast<wxStaticBitmap*>(win) != nullptr)
+            win->SetMinSize(create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize());
     };
 }
 
@@ -431,6 +442,7 @@ void ObjectManipulation::emulate_kill_focus()
 void ObjectManipulation::update_warning_icon_state(const wxString& tooltip)
 {
     m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
+    m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.bmp().GetSize());
     m_fix_throught_netfab_bitmap->SetToolTip(tooltip);
 }
 
@@ -664,7 +676,10 @@ void ObjectManipulation::msw_rescale()
 {
     msw_rescale_word_local_combo(m_word_local_combo);
     m_manifold_warning_bmp.msw_rescale();
-    m_fix_throught_netfab_bitmap->SetBitmap(m_manifold_warning_bmp.bmp());
+
+    const wxString& tooltip = m_fix_throught_netfab_bitmap->GetToolTipText();
+    m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
+    m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0, 0) : m_manifold_warning_bmp.bmp().GetSize());
 
     get_og()->msw_rescale();
 }
diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp
index a17bbf6d3..39b68ea7a 100644
--- a/src/slic3r/GUI/GUI_Utils.hpp
+++ b/src/slic3r/GUI/GUI_Utils.hpp
@@ -72,6 +72,8 @@ public:
         this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) {
             m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
 
+            m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize();
+
             if (!m_can_rescale)
                 return;
 
@@ -124,6 +126,8 @@ private:
     float m_prev_scale_factor;
     bool  m_can_rescale{ true };
 
+    int   m_new_font_point_size;
+
 //    void recalc_font()
 //    {
 //        wxClientDC dc(this);
@@ -148,18 +152,42 @@ private:
         window->Layout();
     }
 
+    void    scale_win_font(wxWindow *window, const int font_point_size)
+    {
+        wxFont new_font(window->GetFont());
+        new_font.SetPointSize(font_point_size);
+        window->SetFont(new_font);
+    }
+
+    // recursive function for scaling fonts for all controls in Window
+    void    scale_controls_fonts(wxWindow *window, const int font_point_size)
+    {
+        auto children = window->GetChildren();
+
+        for (auto child : children) {
+            scale_controls_fonts(child, font_point_size);
+            scale_win_font(child, font_point_size);
+        }
+
+        window->Layout();
+    }
+
     void    rescale(const wxRect &suggested_rect)
     {
         this->Freeze();
-        const float relative_scale_factor = m_scale_factor / m_prev_scale_factor;
+//        const float relative_scale_factor = m_scale_factor / m_prev_scale_factor;
 
         // rescale fonts of all controls
-        scale_controls_fonts(this, relative_scale_factor);
-        this->SetFont(this->GetFont().Scaled(relative_scale_factor));
+//        scale_controls_fonts(this, relative_scale_factor);
+        scale_controls_fonts(this, m_new_font_point_size);
+
+//        this->SetFont(this->GetFont().Scaled(relative_scale_factor));
+        scale_win_font(this, m_new_font_point_size);
 
 
         // rescale normal_font value
-        m_normal_font = m_normal_font.Scaled(relative_scale_factor);
+//         m_normal_font = m_normal_font.Scaled(relative_scale_factor);
+        m_normal_font = this->GetFont();
 
         // An analog of em_unit value from GUI_App.
         m_em_unit = std::max<size_t>(10, 10 * m_scale_factor);
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index 76ba853dc..546e4889f 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -2478,6 +2478,8 @@ void LockButton::msw_rescale()
     m_bmp_lock_off  .msw_rescale();
     m_bmp_unlock_on .msw_rescale();
     m_bmp_unlock_off.msw_rescale();
+
+    enter_button(false);
 }
 
 void LockButton::enter_button(const bool enter)

From 12133f95993ba33064b059e971f27e02fcc046d8 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 19 Jun 2019 11:38:42 +0200
Subject: [PATCH 2/9] Code cleaning and last msw_rescale() improvements

---
 src/slic3r/GUI/GUI_Utils.hpp | 32 +++++++++++---------------------
 src/slic3r/GUI/MainFrame.cpp |  4 +++-
 2 files changed, 14 insertions(+), 22 deletions(-)

diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp
index 39b68ea7a..c47714e46 100644
--- a/src/slic3r/GUI/GUI_Utils.hpp
+++ b/src/slic3r/GUI/GUI_Utils.hpp
@@ -64,6 +64,12 @@ public:
         m_prev_scale_factor = m_scale_factor;
 		m_normal_font = get_default_font_for_dpi(dpi);
 
+        /* Because of default window font is a primary display font, 
+         * We should set correct font for window before getting em_unit value.
+         */
+#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList 
+        this->SetFont(m_normal_font);
+#endif
         // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
         m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
 
@@ -139,19 +145,7 @@ private:
     // check if new scale is differ from previous
     bool    is_new_scale_factor() const { return fabs(m_scale_factor - m_prev_scale_factor) > 0.001; }
 
-    // recursive function for scaling fonts for all controls in Window
-    void    scale_controls_fonts(wxWindow *window, const float scale_f)
-    {
-        auto children = window->GetChildren();
-
-        for (auto child : children) {
-            scale_controls_fonts(child, scale_f);
-            child->SetFont(child->GetFont().Scaled(scale_f));
-        }
-
-        window->Layout();
-    }
-
+    // function for a font scaling of the window
     void    scale_win_font(wxWindow *window, const int font_point_size)
     {
         wxFont new_font(window->GetFont());
@@ -175,22 +169,18 @@ private:
     void    rescale(const wxRect &suggested_rect)
     {
         this->Freeze();
-//        const float relative_scale_factor = m_scale_factor / m_prev_scale_factor;
 
         // rescale fonts of all controls
-//        scale_controls_fonts(this, relative_scale_factor);
         scale_controls_fonts(this, m_new_font_point_size);
-
-//        this->SetFont(this->GetFont().Scaled(relative_scale_factor));
+        // rescale current window font
         scale_win_font(this, m_new_font_point_size);
 
 
-        // rescale normal_font value
-//         m_normal_font = m_normal_font.Scaled(relative_scale_factor);
+        // set normal application font as a current window font
         m_normal_font = this->GetFont();
 
-        // An analog of em_unit value from GUI_App.
-        m_em_unit = std::max<size_t>(10, 10 * m_scale_factor);
+        // update em_unit value for new window font
+        m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
 
         // rescale missed controls sizes and images
         on_dpi_changed(suggested_rect);
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 7e5b3ec05..47d012fdf 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -38,10 +38,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
 {
     // Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
     wxGetApp().update_fonts(this);
+/*
 #ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList 
     this->SetFont(this->normal_font());
 #endif
-
+    // Font is already set in DPIFrame constructor
+*/
     // Load the icon either from the exe, or from the ico file.
 #if _WIN32
     {

From 7c94db063488092607877a54ed3e4f0cdac02745 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 20 Aug 2019 15:49:32 +0200
Subject: [PATCH 3/9] Adding new sla material parameters: (initial) exposition
 min/max

---
 src/libslic3r/PrintConfig.cpp | 32 ++++++++++++++++++++++
 src/libslic3r/PrintConfig.hpp |  8 ++++++
 src/libslic3r/SLAPrint.cpp    | 18 +++++++++++++
 src/slic3r/GUI/Preset.cpp     |  3 ++-
 src/slic3r/GUI/Tab.cpp        | 51 +++++++++++++++++++++++++++++++++++
 5 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 31de80e8b..037b24800 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2412,6 +2412,22 @@ void PrintConfigDef::init_sla_params()
     def->mode = comExpert;
     def->set_default_value(new ConfigOptionInt(10));
 
+    def = this->add("exposure_time_min", coFloat);
+    def->label = L("Minimum exposure time");
+    def->tooltip = L("Minimum exposure time");
+    def->sidetext = L("s");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(0));
+
+    def = this->add("exposure_time_max", coFloat);
+    def->label = L("Maximum exposure time");
+    def->tooltip = L("Maximum exposure time");
+    def->sidetext = L("s");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(100));
+
     def = this->add("exposure_time", coFloat);
     def->label = L("Exposure time");
     def->tooltip = L("Exposure time");
@@ -2419,6 +2435,22 @@ void PrintConfigDef::init_sla_params()
     def->min = 0;
     def->set_default_value(new ConfigOptionFloat(10));
 
+    def = this->add("initial_exposure_time_min", coFloat);
+    def->label = L("Minimum initial exposure time");
+    def->tooltip = L("Minimum initial exposure time");
+    def->sidetext = L("s");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(0));
+
+    def = this->add("initial_exposure_time_max", coFloat);
+    def->label = L("Maximum initial exposure time");
+    def->tooltip = L("Maximum initial exposure time");
+    def->sidetext = L("s");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(150));
+
     def = this->add("initial_exposure_time", coFloat);
     def->label = L("Initial exposure time");
     def->tooltip = L("Initial exposure time");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 081f670e1..ca2a210f2 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -1098,14 +1098,22 @@ class SLAMaterialConfig : public StaticPrintConfig
     STATIC_PRINT_CONFIG_CACHE(SLAMaterialConfig)
 public:
     ConfigOptionFloat                       initial_layer_height;
+    ConfigOptionFloat                       exposure_time_min;
+    ConfigOptionFloat                       exposure_time_max;
     ConfigOptionFloat                       exposure_time;
+    ConfigOptionFloat                       initial_exposure_time_min;
+    ConfigOptionFloat                       initial_exposure_time_max;
     ConfigOptionFloat                       initial_exposure_time;
     ConfigOptionFloats                      material_correction;
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
     {
         OPT_PTR(initial_layer_height);
+        OPT_PTR(exposure_time_min);
+        OPT_PTR(exposure_time_max);
         OPT_PTR(exposure_time);
+        OPT_PTR(initial_exposure_time_min);
+        OPT_PTR(initial_exposure_time_max);
         OPT_PTR(initial_exposure_time);
         OPT_PTR(material_correction);
     }
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 1529f4baf..8e99d6ddc 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -692,6 +692,20 @@ std::string SLAPrint::validate() const
         }
     }
 
+    double expt_max = m_material_config.exposure_time_max.getFloat();
+    double expt_min = m_material_config.exposure_time_min.getFloat();
+    double expt_cur = m_material_config.exposure_time.getFloat();
+
+    if (expt_cur < expt_min || expt_cur > expt_max)
+        return L("Exposition time is out of predefined bounds.");
+
+    double iexpt_max = m_material_config.initial_exposure_time_max.getFloat();
+    double iexpt_min = m_material_config.initial_exposure_time_min.getFloat();
+    double iexpt_cur = m_material_config.initial_exposure_time.getFloat();
+
+    if (iexpt_cur < iexpt_min || iexpt_cur > iexpt_max)
+        return L("Initial exposition time is out of predefined bounds.");
+
     return "";
 }
 
@@ -1586,7 +1600,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
     // Cache the plenty of parameters, which influence the final rasterization only,
     // or they are only notes not influencing the rasterization step.
     static std::unordered_set<std::string> steps_rasterize = {
+        "exposure_time_min",
+        "exposure_time_max",
         "exposure_time",
+        "initial_exposure_time_min",
+        "initial_exposure_time_max",
         "initial_exposure_time",
         "display_width",
         "display_height",
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 833da238a..82124761a 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -500,7 +500,8 @@ const std::vector<std::string>& Preset::sla_material_options()
     if (s_opts.empty()) {
         s_opts = {
             "initial_layer_height",
-            "exposure_time", "initial_exposure_time",
+            "exposure_time_min", "exposure_time_max", "exposure_time",
+            "initial_exposure_time_min", "initial_exposure_time_max", "initial_exposure_time",
             "material_correction",
             "material_notes",
             "default_sla_material_profile",
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 4afd3a116..188bb50cf 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -3614,7 +3614,11 @@ void TabSLAMaterial::build()
     optgroup->append_single_option_line("initial_layer_height");
 
     optgroup = page->new_optgroup(_(L("Exposure")));
+    optgroup->append_single_option_line("exposure_time_min");
+    optgroup->append_single_option_line("exposure_time_max");
     optgroup->append_single_option_line("exposure_time");
+    optgroup->append_single_option_line("initial_exposure_time_min");
+    optgroup->append_single_option_line("initial_exposure_time_max");
     optgroup->append_single_option_line("initial_exposure_time");
 
     optgroup = page->new_optgroup(_(L("Corrections")));
@@ -3679,11 +3683,58 @@ void TabSLAMaterial::reload_config()
     Tab::reload_config();
 }
 
+
+namespace {
+
+enum e_cmp {EQUAL = 1, SMALLER = 2, GREATER = 4, SMALLER_EQ = 3, GREATER_EQ = 5};
+
+void bound_check(Tab &tb, e_cmp cmp, const char *id, const char *boundid)
+{
+    double bound = tb.m_config->opt_float(boundid);
+    double value = tb.m_config->opt_float(id);
+
+    auto boundlabel = tb.m_config->def()->get(boundid)->label;
+    auto valuelabel = tb.m_config->def()->get(id)->label;
+
+    double ddiff = value - bound;
+    int diff = ddiff < 0 ? SMALLER : (std::abs(ddiff) < EPSILON ? EQUAL : GREATER);
+
+    wxString fmt;
+    switch (cmp) {
+    case EQUAL:      fmt = _(L("%s should be equal to %s")); break;
+    case SMALLER:    fmt = _(L("%s should be smaller than %s")); break;
+    case GREATER:    fmt = _(L("%s should be greater than %s")); break;
+    case SMALLER_EQ: fmt = _(L("%s should be smaller or equal to %s")); break;
+    case GREATER_EQ: fmt = _(L("%s should be greater or equal to %s")); break;
+    }
+
+    if ((cmp | diff) != cmp) {
+        wxString msg_text = wxString::Format(fmt, valuelabel, boundlabel);
+
+        wxMessageDialog dialog(tb.parent(), msg_text,
+                               _(L("Value outside bounds")),
+                               wxICON_WARNING | wxOK);
+
+        DynamicPrintConfig new_conf = *tb.m_config;
+        if (dialog.ShowModal() == wxID_OK)
+            new_conf.set_key_value(id, new ConfigOptionFloat(bound));
+
+        tb.load_config(new_conf);
+    }
+};
+
+}
+
 void TabSLAMaterial::update()
 {
     if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
         return;
 
+    bound_check(*this, e_cmp::GREATER_EQ, "exposure_time", "exposure_time_min");
+    bound_check(*this, e_cmp::SMALLER_EQ, "exposure_time", "exposure_time_max");
+    bound_check(*this, e_cmp::GREATER_EQ, "initial_exposure_time", "initial_exposure_time_min");
+    bound_check(*this, e_cmp::SMALLER_EQ, "initial_exposure_time", "initial_exposure_time_max");
+
 // #ys_FIXME. Just a template for this function
 //     m_update_cnt++;
 //     ! something to update

From bafa4d6d1909c3e9541b349129e6929ee962368b Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 20 Aug 2019 16:00:26 +0200
Subject: [PATCH 4/9] Follow up: Adding new sla material parameters...

Small fix for redundant operations.
---
 src/slic3r/GUI/Tab.cpp | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 188bb50cf..030165337 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -3699,16 +3699,17 @@ void bound_check(Tab &tb, e_cmp cmp, const char *id, const char *boundid)
     double ddiff = value - bound;
     int diff = ddiff < 0 ? SMALLER : (std::abs(ddiff) < EPSILON ? EQUAL : GREATER);
 
-    wxString fmt;
-    switch (cmp) {
-    case EQUAL:      fmt = _(L("%s should be equal to %s")); break;
-    case SMALLER:    fmt = _(L("%s should be smaller than %s")); break;
-    case GREATER:    fmt = _(L("%s should be greater than %s")); break;
-    case SMALLER_EQ: fmt = _(L("%s should be smaller or equal to %s")); break;
-    case GREATER_EQ: fmt = _(L("%s should be greater or equal to %s")); break;
-    }
-
     if ((cmp | diff) != cmp) {
+        wxString fmt;
+        
+        switch (cmp) {
+        case EQUAL:      fmt = _(L("%s should be equal to %s")); break;
+        case SMALLER:    fmt = _(L("%s should be smaller than %s")); break;
+        case GREATER:    fmt = _(L("%s should be greater than %s")); break;
+        case SMALLER_EQ: fmt = _(L("%s should be smaller or equal to %s")); break;
+        case GREATER_EQ: fmt = _(L("%s should be greater or equal to %s")); break;
+        }
+        
         wxString msg_text = wxString::Format(fmt, valuelabel, boundlabel);
 
         wxMessageDialog dialog(tb.parent(), msg_text,

From fd3fe75d1ca4ddf1a36eb89d7210a4ec0e8f4afa Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 20 Aug 2019 16:19:30 +0200
Subject: [PATCH 5/9] Reworked the rename_file() function on Windows to work
 reliably and atomically. The code was taken from the llvm project, it is
 complex and hopefully it covers all the Windows file system quirks. Vojtech
 has highest hopes, that this will fix the various PrusaSlicer.ini file
 corruptions.

Enabled the locales switching and error handling on Linux as well,
where now the missing locales are reported and running the locale-gen
tool is recommended.
---
 src/PrusaSlicer.cpp                  |   9 +-
 src/libslic3r/GCode.cpp              |   2 +-
 src/libslic3r/GCodeTimeEstimator.cpp |   2 +-
 src/libslic3r/Utils.hpp              |   2 +-
 src/libslic3r/utils.cpp              | 288 ++++++++++++++++++++++-----
 src/slic3r/GUI/AppConfig.cpp         |   7 +-
 6 files changed, 248 insertions(+), 62 deletions(-)

diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 72cbe150f..92ff65a84 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -66,7 +66,12 @@ int CLI::run(int argc, char **argv)
         boost::nowide::nowide_filesystem();
     } catch (const std::runtime_error& ex) {
         std::string caption = std::string(SLIC3R_APP_NAME) + " Error";
-        std::string text = std::string("An error occured while setting up locale.\n") + SLIC3R_APP_NAME + " will now terminate.\n\n" + ex.what();
+        std::string text = std::string("An error occured while setting up locale.\n") + (
+#if !defined(_WIN32) && !defined(__APPLE__)
+        	// likely some linux system
+        	"You may need to reconfigure the missing locales, likely by running the \"locale-gen\"" and \"dpkg-reconfigure locales\" commands.\n"
+#endif
+        	SLIC3R_APP_NAME " will now terminate.\n\n") + ex.what();
     #ifdef SLIC3R_GUI
         if (m_actions.empty())
             MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);
@@ -426,7 +431,7 @@ int CLI::run(int argc, char **argv)
                             outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
                             sla_print.export_raster(outfile_final);
                         }
-                        if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
+                        if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final)) {
                             boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
                             return 1;
                         }
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 7a6f686df..8a007023b 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -607,7 +607,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
         m_analyzer.reset();
     }
 
-    if (rename_file(path_tmp, path) != 0)
+    if (rename_file(path_tmp, path))
         throw std::runtime_error(
             std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
             "Is " + path_tmp + " locked?" + '\n');
diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp
index 8a2e1266a..a03d3c7c8 100644
--- a/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/src/libslic3r/GCodeTimeEstimator.cpp
@@ -390,7 +390,7 @@ namespace Slic3r {
         fclose(out);
         in.close();
 
-        if (rename_file(path_tmp, filename) != 0)
+        if (rename_file(path_tmp, filename))
             throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
             "Is " + path_tmp + " locked?" + '\n');
 
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index b19027826..2b1fdb241 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -61,7 +61,7 @@ extern std::string normalize_utf8_nfc(const char *src);
 // Safely rename a file even if the target exists.
 // On Windows, the file explorer (or anti-virus or whatever else) often locks the file
 // for a short while, so the file may not be movable. Retry while we see recoverable errors.
-extern int rename_file(const std::string &from, const std::string &to);
+extern std::error_code rename_file(const std::string &from, const std::string &to);
 
 // Copy a file, adjust the access attributes, so that the target is writable.
 extern int copy_file(const std::string &from, const std::string &to);
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index 8fcd611ac..e26ed3839 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -173,67 +173,247 @@ const std::string& data_dir()
     return g_data_dir;
 }
 
+#ifdef _WIN32
+// The following helpers are borrowed from the LLVM project https://github.com/llvm
+namespace WindowsSupport
+{
+	template <typename HandleTraits>
+	class ScopedHandle {
+		typedef typename HandleTraits::handle_type handle_type;
+		handle_type Handle;
+		ScopedHandle(const ScopedHandle &other) = delete;
+		void operator=(const ScopedHandle &other) = delete;
+	public:
+		ScopedHandle() : Handle(HandleTraits::GetInvalid()) {}
+	  	explicit ScopedHandle(handle_type h) : Handle(h) {}
+	  	~ScopedHandle() { if (HandleTraits::IsValid(Handle)) HandleTraits::Close(Handle); }
+	  	handle_type take() {
+	    	handle_type t = Handle;
+	    	Handle = HandleTraits::GetInvalid();
+	    	return t;
+	  	}
+	  	ScopedHandle &operator=(handle_type h) {
+	    	if (HandleTraits::IsValid(Handle))
+	      		HandleTraits::Close(Handle);
+	    	Handle = h;
+	    	return *this;
+	  	}
+	  	// True if Handle is valid.
+	  	explicit operator bool() const { return HandleTraits::IsValid(Handle) ? true : false; }
+	  	operator handle_type() const { return Handle; }
+	};
+
+	struct CommonHandleTraits {
+	  	typedef HANDLE handle_type;
+	  	static handle_type GetInvalid() { return INVALID_HANDLE_VALUE; }
+	  	static void Close(handle_type h) { ::CloseHandle(h); }
+	  	static bool IsValid(handle_type h) { return h != GetInvalid(); }
+	};
+
+	typedef ScopedHandle<CommonHandleTraits> ScopedFileHandle;
+
+	std::error_code map_windows_error(unsigned windows_error_code)
+	{
+		#define MAP_ERR_TO_COND(x, y) case x: return std::make_error_code(std::errc::y)
+		switch (windows_error_code) {
+			MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied);
+			MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists);
+			MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device);
+			MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long);
+			MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy);
+			MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy);
+			MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied);
+			MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error);
+			MAP_ERR_TO_COND(ERROR_CANTREAD, io_error);
+			MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error);
+			MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied);
+			MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device);
+			MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy);
+			MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty);
+			MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument);
+			MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device);
+			MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists);
+			MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory);
+			MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device);
+			MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied);
+			MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device);
+			MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported);
+			MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument);
+			MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument);
+			MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available);
+			MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available);
+			MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument);
+			MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied);
+			MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory);
+			MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again);
+			MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error);
+			MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy);
+			MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory);
+			MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory);
+			MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory);
+			MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error);
+			MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again);
+			MAP_ERR_TO_COND(ERROR_SEEK, io_error);
+			MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied);
+			MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open);
+			MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error);
+			MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied);
+			MAP_ERR_TO_COND(WSAEACCES, permission_denied);
+			MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor);
+			MAP_ERR_TO_COND(WSAEFAULT, bad_address);
+			MAP_ERR_TO_COND(WSAEINTR, interrupted);
+			MAP_ERR_TO_COND(WSAEINVAL, invalid_argument);
+			MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open);
+			MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long);
+		default:
+			return std::error_code(windows_error_code, std::system_category());
+		}
+		#undef MAP_ERR_TO_COND
+	}
+
+	static std::error_code rename_internal(HANDLE from_handle, const std::wstring &wide_to, bool replace_if_exists)
+	{
+		std::vector<char> rename_info_buf(sizeof(FILE_RENAME_INFO) - sizeof(wchar_t) + (wide_to.size() * sizeof(wchar_t)));
+		FILE_RENAME_INFO &rename_info = *reinterpret_cast<FILE_RENAME_INFO*>(rename_info_buf.data());
+		rename_info.ReplaceIfExists = replace_if_exists;
+		rename_info.RootDirectory = 0;
+		rename_info.FileNameLength = DWORD(wide_to.size() * sizeof(wchar_t));
+		std::copy(wide_to.begin(), wide_to.end(), &rename_info.FileName[0]);
+
+		::SetLastError(ERROR_SUCCESS);
+		if (! ::SetFileInformationByHandle(from_handle, FileRenameInfo, &rename_info, (DWORD)rename_info_buf.size())) {
+			unsigned Error = GetLastError();
+			if (Error == ERROR_SUCCESS)
+		  		Error = ERROR_CALL_NOT_IMPLEMENTED; // Wine doesn't always set error code.
+			return map_windows_error(Error);
+		}
+
+		return std::error_code();
+	}
+
+	static std::error_code real_path_from_handle(HANDLE H, std::wstring &buffer)
+	{
+		buffer.resize(MAX_PATH + 1);
+		DWORD CountChars = ::GetFinalPathNameByHandleW(H, (LPWSTR)buffer.data(), (DWORD)buffer.size() - 1, FILE_NAME_NORMALIZED);
+	  	if (CountChars > buffer.size()) {
+	    	// The buffer wasn't big enough, try again.  In this case the return value *does* indicate the size of the null terminator.
+	    	buffer.resize((size_t)CountChars);
+	    	CountChars = ::GetFinalPathNameByHandleW(H, (LPWSTR)buffer.data(), (DWORD)buffer.size() - 1, FILE_NAME_NORMALIZED);
+	  	}
+	  	if (CountChars == 0)
+	    	return map_windows_error(GetLastError());
+	  	buffer.resize(CountChars);
+	  	return std::error_code();
+	}
+
+	std::error_code rename(const std::string &from, const std::string &to)
+	{
+		// Convert to utf-16.
+		std::wstring wide_from = boost::nowide::widen(from);
+		std::wstring wide_to   = boost::nowide::widen(to);
+
+		ScopedFileHandle from_handle;
+		// Retry this a few times to defeat badly behaved file system scanners.
+		for (unsigned retry = 0; retry != 200; ++ retry) {
+			if (retry != 0)
+		  		::Sleep(10);
+			from_handle = ::CreateFileW((LPWSTR)wide_from.data(), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+			if (from_handle)
+		  		break;
+		}
+		if (! from_handle)
+			return map_windows_error(GetLastError());
+
+		// We normally expect this loop to succeed after a few iterations. If it
+		// requires more than 200 tries, it's more likely that the failures are due to
+		// a true error, so stop trying.
+		for (unsigned retry = 0; retry != 200; ++ retry) {
+			auto errcode = rename_internal(from_handle, wide_to, true);
+
+			if (errcode == std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category())) {
+		  		// Wine doesn't support SetFileInformationByHandle in rename_internal.
+		  		// Fall back to MoveFileEx.
+		  		if (std::error_code errcode2 = real_path_from_handle(from_handle, wide_from))
+		    		return errcode2;
+		  		if (::MoveFileExW((LPCWSTR)wide_from.data(), (LPCWSTR)wide_to.data(), MOVEFILE_REPLACE_EXISTING))
+		    		return std::error_code();
+		  		return map_windows_error(GetLastError());
+			}
+
+			if (! errcode || errcode != std::errc::permission_denied)
+		  		return errcode;
+
+			// The destination file probably exists and is currently open in another
+			// process, either because the file was opened without FILE_SHARE_DELETE or
+			// it is mapped into memory (e.g. using MemoryBuffer). Rename it in order to
+			// move it out of the way of the source file. Use FILE_FLAG_DELETE_ON_CLOSE
+			// to arrange for the destination file to be deleted when the other process
+			// closes it.
+			ScopedFileHandle to_handle(::CreateFileW((LPCWSTR)wide_to.data(), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL));
+			if (! to_handle) {
+				auto errcode = map_windows_error(GetLastError());
+				// Another process might have raced with us and moved the existing file
+				// out of the way before we had a chance to open it. If that happens, try
+				// to rename the source file again.
+				if (errcode == std::errc::no_such_file_or_directory)
+					continue;
+				return errcode;
+			}
+
+			BY_HANDLE_FILE_INFORMATION FI;
+			if (! ::GetFileInformationByHandle(to_handle, &FI))
+		  		return map_windows_error(GetLastError());
+
+			// Try to find a unique new name for the destination file.
+			for (unsigned unique_id = 0; unique_id != 200; ++ unique_id) {
+				std::wstring tmp_filename = wide_to + L".tmp" + std::to_wstring(unique_id);
+				std::error_code errcode = rename_internal(to_handle, tmp_filename, false);
+				if (errcode) {
+					if (errcode == std::make_error_code(std::errc::file_exists) || errcode == std::make_error_code(std::errc::permission_denied)) {
+						// Again, another process might have raced with us and moved the file
+						// before we could move it. Check whether this is the case, as it
+						// might have caused the permission denied error. If that was the
+						// case, we don't need to move it ourselves.
+						ScopedFileHandle to_handle2(::CreateFileW((LPCWSTR)wide_to.data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+						if (! to_handle2) {
+							auto errcode = map_windows_error(GetLastError());
+							if (errcode == std::errc::no_such_file_or_directory)
+						  		break;
+							return errcode;
+						}
+						BY_HANDLE_FILE_INFORMATION FI2;
+						if (! ::GetFileInformationByHandle(to_handle2, &FI2))
+							return map_windows_error(GetLastError());
+						if (FI.nFileIndexHigh != FI2.nFileIndexHigh || FI.nFileIndexLow != FI2.nFileIndexLow || FI.dwVolumeSerialNumber != FI2.dwVolumeSerialNumber)
+							break;
+						continue;
+					}
+					return errcode;
+				}
+				break;
+			}
+
+			// Okay, the old destination file has probably been moved out of the way at
+			// this point, so try to rename the source file again. Still, another
+			// process might have raced with us to create and open the destination
+			// file, so we need to keep doing this until we succeed.
+		}
+
+		// The most likely root cause.
+		return std::make_error_code(std::errc::permission_denied);
+	}
+} // namespace WindowsSupport
+#endif /* _WIN32 */
 
 // borrowed from LVVM lib/Support/Windows/Path.inc
-int rename_file(const std::string &from, const std::string &to)
+std::error_code rename_file(const std::string &from, const std::string &to)
 {
-    int ec = 0;
-
 #ifdef _WIN32
-
-	// Convert to utf-16.
-    std::wstring wide_from = boost::nowide::widen(from);
-    std::wstring wide_to   = boost::nowide::widen(to);
-
-    // Retry while we see recoverable errors.
-    // System scanners (eg. indexer) might open the source file when it is written
-    // and closed.
-    bool TryReplace = true;
-
-    // This loop may take more than 2000 x 1ms to finish.
-    for (int i = 0; i < 2000; ++ i) {
-        if (i > 0)
-            // Sleep 1ms
-            ::Sleep(1);
-        if (TryReplace) {
-            // Try ReplaceFile first, as it is able to associate a new data stream
-            // with the destination even if the destination file is currently open.
-            if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL))
-                return 0;
-            DWORD ReplaceError = ::GetLastError();
-            ec = -1; // ReplaceError
-            // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or
-            // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW().
-            if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT ||
-                ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) {
-                TryReplace = false;
-                continue;
-            }
-            // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry
-            // using ReplaceFileW().
-            if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED)
-                continue;
-            // We get ERROR_FILE_NOT_FOUND if the destination file is missing.
-            // MoveFileEx can handle this case.
-            if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION)
-                break;
-        }
-        if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING))
-            return 0;
-        DWORD MoveError = ::GetLastError();
-        ec = -1; // MoveError
-        if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION)
-            break;
-    }
-
+	return WindowsSupport::rename(from, to);
 #else
-
 	boost::nowide::remove(to.c_str());
-	ec = boost::nowide::rename(from.c_str(), to.c_str());
-
+	return std::make_error_code(static_cast<std::errc>(boost::nowide::rename(from.c_str(), to.c_str())));
 #endif
-
-    return ec;
 }
 
 int copy_file(const std::string &from, const std::string &to)
diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp
index 7ca8c3e5e..96213447c 100644
--- a/src/slic3r/GUI/AppConfig.cpp
+++ b/src/slic3r/GUI/AppConfig.cpp
@@ -98,9 +98,10 @@ void AppConfig::load()
         pt::read_ini(ifs, tree);
     } catch (pt::ptree_error& ex) {
         // Error while parsing config file. We'll customize the error message and rethrow to be displayed.
-        throw std::runtime_error(wxString::Format(_(L("Error parsing config file, it is probably corrupted. "
-            "Try to manualy delete the file. Your user profiles will not be affected.\n\n%s\n\n%s")),
-            AppConfig::config_path(), ex.what()).ToStdString());
+        throw std::runtime_error(
+        	_utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. "
+                    "Try to manualy delete the file to recover from the error. Your user profiles will not be affected.")) + 
+        	"\n\n" + AppConfig::config_path() + "\n\n" + ex.what());
     }
 
     // 2) Parse the property_tree, extract the sections and key / value pairs.

From 88dcb7f366324225e715a67fa231fff0bda584a4 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 20 Aug 2019 16:38:03 +0200
Subject: [PATCH 6/9] Checking for OpenGL driver version in the GUI slicer and
 giving some reasonable advice to the user in case OpenGL < 2.0 was detected.

---
 src/PrusaSlicer.cpp        |  3 +--
 src/slic3r/GUI/GUI_App.cpp | 14 ++++++++++++++
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 92ff65a84..81422f4a8 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -60,7 +60,6 @@ PrinterTechnology get_printer_technology(const DynamicConfig &config)
 
 int CLI::run(int argc, char **argv)
 {
-#ifdef _WIN32
 	// Switch boost::filesystem to utf8.
     try {
         boost::nowide::nowide_filesystem();
@@ -74,12 +73,12 @@ int CLI::run(int argc, char **argv)
         	SLIC3R_APP_NAME " will now terminate.\n\n") + ex.what();
     #ifdef SLIC3R_GUI
         if (m_actions.empty())
+        	// Empty actions means Slicer is executed in the GUI mode. Show a GUI message.
             MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);
     #endif
         boost::nowide::cerr << text.c_str() << std::endl;
         return 1;
     }
-#endif
 
 	if (! this->setup(argc, argv))
 		return 1;
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 9b4522356..dff9fc1a9 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -293,6 +293,20 @@ bool GUI_App::on_init_inner()
                 config_wizard_startup(app_conf_exists);
                 preset_updater->slic3r_update_notify();
                 preset_updater->sync(preset_bundle);
+                const GLCanvas3DManager::GLInfo &glinfo = GLCanvas3DManager::get_gl_info();
+                if (! glinfo.is_version_greater_or_equal_to(2, 0)) {
+                	// Complain about the OpenGL version.
+                	wxString message = wxString::Format(
+                		_(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n"
+                			"while OpenGL version %s, render %s, vendor %s was detected.")), wxString(glinfo.get_version()), wxString(glinfo.get_renderer()), wxString(glinfo.get_vendor()));
+                	message += "\n";
+                	message += _(L("You may need to update your graphics card driver."));
+#ifdef _WIN32
+                	message += "\n";
+                	message += _(L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter."));
+#endif
+                	wxMessageBox(message, wxString("PrusaSlicer - ") + _(L("Unsupported OpenGL version")), wxOK | wxICON_ERROR);
+                }
             });
         }
     });

From b58713c06ff154b8d421343496a64d29ffb9b1db Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 20 Aug 2019 17:24:48 +0200
Subject: [PATCH 7/9] SLA exposure bounds to printer params.

---
 src/libslic3r/PrintConfig.cpp |  8 +--
 src/libslic3r/PrintConfig.hpp | 16 +++---
 src/libslic3r/SLAPrint.cpp    | 20 ++++----
 src/slic3r/GUI/Preset.cpp     |  6 ++-
 src/slic3r/GUI/Tab.cpp        | 94 +++++++++--------------------------
 5 files changed, 50 insertions(+), 94 deletions(-)

diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 037b24800..8f56c1b83 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2412,7 +2412,7 @@ void PrintConfigDef::init_sla_params()
     def->mode = comExpert;
     def->set_default_value(new ConfigOptionInt(10));
 
-    def = this->add("exposure_time_min", coFloat);
+    def = this->add("min_exposure_time", coFloat);
     def->label = L("Minimum exposure time");
     def->tooltip = L("Minimum exposure time");
     def->sidetext = L("s");
@@ -2420,7 +2420,7 @@ void PrintConfigDef::init_sla_params()
     def->mode = comExpert;
     def->set_default_value(new ConfigOptionFloat(0));
 
-    def = this->add("exposure_time_max", coFloat);
+    def = this->add("max_exposure_time", coFloat);
     def->label = L("Maximum exposure time");
     def->tooltip = L("Maximum exposure time");
     def->sidetext = L("s");
@@ -2435,7 +2435,7 @@ void PrintConfigDef::init_sla_params()
     def->min = 0;
     def->set_default_value(new ConfigOptionFloat(10));
 
-    def = this->add("initial_exposure_time_min", coFloat);
+    def = this->add("min_initial_exposure_time", coFloat);
     def->label = L("Minimum initial exposure time");
     def->tooltip = L("Minimum initial exposure time");
     def->sidetext = L("s");
@@ -2443,7 +2443,7 @@ void PrintConfigDef::init_sla_params()
     def->mode = comExpert;
     def->set_default_value(new ConfigOptionFloat(0));
 
-    def = this->add("initial_exposure_time_max", coFloat);
+    def = this->add("max_initial_exposure_time", coFloat);
     def->label = L("Maximum initial exposure time");
     def->tooltip = L("Maximum initial exposure time");
     def->sidetext = L("s");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index ca2a210f2..35025fcd1 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -1098,22 +1098,14 @@ class SLAMaterialConfig : public StaticPrintConfig
     STATIC_PRINT_CONFIG_CACHE(SLAMaterialConfig)
 public:
     ConfigOptionFloat                       initial_layer_height;
-    ConfigOptionFloat                       exposure_time_min;
-    ConfigOptionFloat                       exposure_time_max;
     ConfigOptionFloat                       exposure_time;
-    ConfigOptionFloat                       initial_exposure_time_min;
-    ConfigOptionFloat                       initial_exposure_time_max;
     ConfigOptionFloat                       initial_exposure_time;
     ConfigOptionFloats                      material_correction;
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
     {
         OPT_PTR(initial_layer_height);
-        OPT_PTR(exposure_time_min);
-        OPT_PTR(exposure_time_max);
         OPT_PTR(exposure_time);
-        OPT_PTR(initial_exposure_time_min);
-        OPT_PTR(initial_exposure_time_max);
         OPT_PTR(initial_exposure_time);
         OPT_PTR(material_correction);
     }
@@ -1139,6 +1131,10 @@ public:
     ConfigOptionFloat                       fast_tilt_time;
     ConfigOptionFloat                       slow_tilt_time;
     ConfigOptionFloat                       area_fill;
+    ConfigOptionFloat                       min_exposure_time;
+    ConfigOptionFloat                       max_exposure_time;
+    ConfigOptionFloat                       min_initial_exposure_time;
+    ConfigOptionFloat                       max_initial_exposure_time;
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
     {
@@ -1158,6 +1154,10 @@ protected:
         OPT_PTR(fast_tilt_time);
         OPT_PTR(slow_tilt_time);
         OPT_PTR(area_fill);
+        OPT_PTR(min_exposure_time);
+        OPT_PTR(max_exposure_time);
+        OPT_PTR(min_initial_exposure_time);
+        OPT_PTR(max_initial_exposure_time);
     }
 };
 
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 8e99d6ddc..21aec8384 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -692,19 +692,19 @@ std::string SLAPrint::validate() const
         }
     }
 
-    double expt_max = m_material_config.exposure_time_max.getFloat();
-    double expt_min = m_material_config.exposure_time_min.getFloat();
+    double expt_max = m_printer_config.max_exposure_time.getFloat();
+    double expt_min = m_printer_config.min_exposure_time.getFloat();
     double expt_cur = m_material_config.exposure_time.getFloat();
 
     if (expt_cur < expt_min || expt_cur > expt_max)
-        return L("Exposition time is out of predefined bounds.");
+        return L("Exposition time is out of printer profile bounds.");
 
-    double iexpt_max = m_material_config.initial_exposure_time_max.getFloat();
-    double iexpt_min = m_material_config.initial_exposure_time_min.getFloat();
+    double iexpt_max = m_printer_config.max_initial_exposure_time.getFloat();
+    double iexpt_min = m_printer_config.min_initial_exposure_time.getFloat();
     double iexpt_cur = m_material_config.initial_exposure_time.getFloat();
 
     if (iexpt_cur < iexpt_min || iexpt_cur > iexpt_max)
-        return L("Initial exposition time is out of predefined bounds.");
+        return L("Initial exposition time is out of printer profile bounds.");
 
     return "";
 }
@@ -1600,11 +1600,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
     // Cache the plenty of parameters, which influence the final rasterization only,
     // or they are only notes not influencing the rasterization step.
     static std::unordered_set<std::string> steps_rasterize = {
-        "exposure_time_min",
-        "exposure_time_max",
+        "min_exposure_time",
+        "max_exposure_time",
         "exposure_time",
-        "initial_exposure_time_min",
-        "initial_exposure_time_max",
+        "min_initial_exposure_time",
+        "max_initial_exposure_time",
         "initial_exposure_time",
         "display_width",
         "display_height",
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 82124761a..64793630c 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -500,8 +500,8 @@ const std::vector<std::string>& Preset::sla_material_options()
     if (s_opts.empty()) {
         s_opts = {
             "initial_layer_height",
-            "exposure_time_min", "exposure_time_max", "exposure_time",
-            "initial_exposure_time_min", "initial_exposure_time_max", "initial_exposure_time",
+            "exposure_time",
+            "initial_exposure_time",
             "material_correction",
             "material_notes",
             "default_sla_material_profile",
@@ -527,6 +527,8 @@ const std::vector<std::string>& Preset::sla_printer_options()
             "relative_correction",
             "absolute_correction",
             "gamma_correction",
+            "min_exposure_time", "max_exposure_time",
+            "min_initial_exposure_time", "max_initial_exposure_time",
             "print_host", "printhost_apikey", "printhost_cafile",
             "printer_notes",
             "inherits"
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 030165337..368854222 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -838,7 +838,7 @@ static wxString support_combo_value_for_config(const DynamicPrintConfig &config,
 
 static wxString pad_combo_value_for_config(const DynamicPrintConfig &config)
 {
-	return config.opt_bool("pad_enable") ? (config.opt_bool("pad_zero_elevation") ? _("Around object") : _("Below object")) : _("None");
+    return config.opt_bool("pad_enable") ? (config.opt_bool("pad_zero_elevation") ? _("Around object") : _("Below object")) : _("None");
 }
 
 void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
@@ -860,8 +860,8 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
             (opt_key == "supports_enable"  || opt_key == "support_buildplate_only"))
         og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff));
 
-	if (! is_fff && (opt_key == "pad_enable" || opt_key == "pad_zero_elevation"))
-		og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
+    if (! is_fff && (opt_key == "pad_enable" || opt_key == "pad_zero_elevation"))
+        og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
 
     if (opt_key == "brim_width")
     {
@@ -998,7 +998,7 @@ void Tab::update_frequently_changed_parameters()
 
     og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff));
     if (! is_fff)
-    	og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
+        og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
 
     const std::string updated_value_key = is_fff ? "fill_density" : "pad_enable";
 
@@ -1772,13 +1772,13 @@ void TabFilament::reload_config()
 
 void TabFilament::update_volumetric_flow_preset_hints()
 {
-	wxString text;
-	try {
-		text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
-	} catch (std::exception &ex) {
-		text = _(L("Volumetric flow hints not available\n\n")) + from_u8(ex.what());
-	}
-	m_volumetric_speed_description_line->SetText(text);
+    wxString text;
+    try {
+        text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
+    } catch (std::exception &ex) {
+        text = _(L("Volumetric flow hints not available\n\n")) + from_u8(ex.what());
+    }
+    m_volumetric_speed_description_line->SetText(text);
 }
 
 void TabFilament::update()
@@ -1788,9 +1788,9 @@ void TabFilament::update()
 
     m_update_cnt++;
 
-	wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
-	m_cooling_description_line->SetText(text);
-	this->update_volumetric_flow_preset_hints();
+    wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
+    m_cooling_description_line->SetText(text);
+    this->update_volumetric_flow_preset_hints();
     Layout();
 
     bool cooling = m_config->opt_bool("cooling", 0);
@@ -1812,8 +1812,8 @@ void TabFilament::update()
 
 void TabFilament::OnActivate()
 {
-	this->update_volumetric_flow_preset_hints();
-	Tab::OnActivate();
+    this->update_volumetric_flow_preset_hints();
+    Tab::OnActivate();
 }
 
 wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticText)
@@ -2290,6 +2290,12 @@ void TabPrinter::build_sla()
     optgroup->append_single_option_line("absolute_correction");
     optgroup->append_single_option_line("gamma_correction");
 
+    optgroup = page->new_optgroup(_(L("Exposure")));
+    optgroup->append_single_option_line("min_exposure_time");
+    optgroup->append_single_option_line("max_exposure_time");
+    optgroup->append_single_option_line("min_initial_exposure_time");
+    optgroup->append_single_option_line("max_initial_exposure_time");
+
     optgroup = page->new_optgroup(_(L("Print Host upload")));
     build_printhost(optgroup.get());
 
@@ -2560,7 +2566,7 @@ void TabPrinter::build_unregular_pages()
             optgroup = page->new_optgroup(_(L("Preview")));
 
             auto reset_to_filament_color = [this, extruder_idx](wxWindow* parent) {
-                add_scaled_button(parent, &m_reset_to_filament_color, "undo", 
+                add_scaled_button(parent, &m_reset_to_filament_color, "undo",
                                   _(L("Reset to Filament Color")), wxBU_LEFT | wxBU_EXACTFIT);
                 ScalableButton* btn = m_reset_to_filament_color;
                 btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
@@ -2571,7 +2577,7 @@ void TabPrinter::build_unregular_pages()
                 {
                     std::vector<std::string> colors = static_cast<const ConfigOptionStrings*>(m_config->option("extruder_colour"))->values;
                     colors[extruder_idx] = "";
-                        
+
                     DynamicPrintConfig new_conf = *m_config;
                     new_conf.set_key_value("extruder_colour", new ConfigOptionStrings(colors));
                     load_config(new_conf);
@@ -3614,11 +3620,7 @@ void TabSLAMaterial::build()
     optgroup->append_single_option_line("initial_layer_height");
 
     optgroup = page->new_optgroup(_(L("Exposure")));
-    optgroup->append_single_option_line("exposure_time_min");
-    optgroup->append_single_option_line("exposure_time_max");
     optgroup->append_single_option_line("exposure_time");
-    optgroup->append_single_option_line("initial_exposure_time_min");
-    optgroup->append_single_option_line("initial_exposure_time_max");
     optgroup->append_single_option_line("initial_exposure_time");
 
     optgroup = page->new_optgroup(_(L("Corrections")));
@@ -3683,59 +3685,11 @@ void TabSLAMaterial::reload_config()
     Tab::reload_config();
 }
 
-
-namespace {
-
-enum e_cmp {EQUAL = 1, SMALLER = 2, GREATER = 4, SMALLER_EQ = 3, GREATER_EQ = 5};
-
-void bound_check(Tab &tb, e_cmp cmp, const char *id, const char *boundid)
-{
-    double bound = tb.m_config->opt_float(boundid);
-    double value = tb.m_config->opt_float(id);
-
-    auto boundlabel = tb.m_config->def()->get(boundid)->label;
-    auto valuelabel = tb.m_config->def()->get(id)->label;
-
-    double ddiff = value - bound;
-    int diff = ddiff < 0 ? SMALLER : (std::abs(ddiff) < EPSILON ? EQUAL : GREATER);
-
-    if ((cmp | diff) != cmp) {
-        wxString fmt;
-        
-        switch (cmp) {
-        case EQUAL:      fmt = _(L("%s should be equal to %s")); break;
-        case SMALLER:    fmt = _(L("%s should be smaller than %s")); break;
-        case GREATER:    fmt = _(L("%s should be greater than %s")); break;
-        case SMALLER_EQ: fmt = _(L("%s should be smaller or equal to %s")); break;
-        case GREATER_EQ: fmt = _(L("%s should be greater or equal to %s")); break;
-        }
-        
-        wxString msg_text = wxString::Format(fmt, valuelabel, boundlabel);
-
-        wxMessageDialog dialog(tb.parent(), msg_text,
-                               _(L("Value outside bounds")),
-                               wxICON_WARNING | wxOK);
-
-        DynamicPrintConfig new_conf = *tb.m_config;
-        if (dialog.ShowModal() == wxID_OK)
-            new_conf.set_key_value(id, new ConfigOptionFloat(bound));
-
-        tb.load_config(new_conf);
-    }
-};
-
-}
-
 void TabSLAMaterial::update()
 {
     if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
         return;
 
-    bound_check(*this, e_cmp::GREATER_EQ, "exposure_time", "exposure_time_min");
-    bound_check(*this, e_cmp::SMALLER_EQ, "exposure_time", "exposure_time_max");
-    bound_check(*this, e_cmp::GREATER_EQ, "initial_exposure_time", "initial_exposure_time_min");
-    bound_check(*this, e_cmp::SMALLER_EQ, "initial_exposure_time", "initial_exposure_time_max");
-
 // #ys_FIXME. Just a template for this function
 //     m_update_cnt++;
 //     ! something to update

From 775a54846fc01ec9e977bad7cfa810ac1789d5b0 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 20 Aug 2019 17:46:19 +0200
Subject: [PATCH 8/9] Fixed compilation of Win32 message boxes on unix systems.

---
 src/PrusaSlicer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 81422f4a8..a75635ae6 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -71,7 +71,7 @@ int CLI::run(int argc, char **argv)
         	"You may need to reconfigure the missing locales, likely by running the \"locale-gen\"" and \"dpkg-reconfigure locales\" commands.\n"
 #endif
         	SLIC3R_APP_NAME " will now terminate.\n\n") + ex.what();
-    #ifdef SLIC3R_GUI
+    #if defined(_WIN32) && defined(SLIC3R_GUI)
         if (m_actions.empty())
         	// Empty actions means Slicer is executed in the GUI mode. Show a GUI message.
             MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);

From 6a2265150196a791e493ee16456ecc5ecdd85f6c Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Tue, 20 Aug 2019 20:24:37 +0200
Subject: [PATCH 9/9] Fixed a typo preventing compilation on Linux

---
 src/PrusaSlicer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index a75635ae6..3c0f27f4d 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -68,7 +68,7 @@ int CLI::run(int argc, char **argv)
         std::string text = std::string("An error occured while setting up locale.\n") + (
 #if !defined(_WIN32) && !defined(__APPLE__)
         	// likely some linux system
-        	"You may need to reconfigure the missing locales, likely by running the \"locale-gen\"" and \"dpkg-reconfigure locales\" commands.\n"
+            "You may need to reconfigure the missing locales, likely by running the \"locale-gen\" and \"dpkg-reconfigure locales\" commands.\n"
 #endif
         	SLIC3R_APP_NAME " will now terminate.\n\n") + ex.what();
     #if defined(_WIN32) && defined(SLIC3R_GUI)