From ea0cb4d761d83ce9b94737486e27e6cf6c35cedb Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 16 Mar 2020 12:49:39 +0100
Subject: [PATCH 01/55] Sidebar can be hidden/shown from menu "View/Collapse
 sidebar"

---
 src/slic3r/GUI/MainFrame.cpp |  3 +++
 src/slic3r/GUI/Plater.cpp    | 25 +++++++++++++++++++++++++
 src/slic3r/GUI/Plater.hpp    |  5 +++++
 3 files changed, 33 insertions(+)

diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 1e22359ab..3a90063c2 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -757,6 +757,9 @@ void MainFrame::init_menubar()
         append_menu_check_item(viewMenu, wxID_ANY, _(L("Show &labels")) + sep + "E", _(L("Show object/instance labels in 3D scene")),
             [this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
             [this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
+        append_menu_check_item(viewMenu, wxID_ANY, _(L("&Collapse sidebar")), _(L("Collapse sidebar")),
+            [this](wxCommandEvent&) { m_plater->collapse_sidebur(!m_plater->is_sidebar_collapsed()); }, this,
+            [this]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
     }
 
     // Help menu
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 035aabf6e..104ecdac8 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -718,6 +718,8 @@ struct Sidebar::priv
     ScalableButton *btn_remove_device;
 	ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
 
+    bool is_collapsed {false};
+
     priv(Plater *plater) : plater(plater) {}
     ~priv();
 
@@ -1360,6 +1362,20 @@ void Sidebar::update_mode()
     Layout();
 }
 
+bool Sidebar::is_collapsed() { return p->is_collapsed; }
+
+void Sidebar::collapse(bool collapse)
+{
+    p->is_collapsed = collapse;
+
+    this->Show(!collapse);
+    p->plater->Layout();
+
+    // save collapsing state to the AppConfig
+    wxGetApp().app_config->set("collapsed_sidebar", collapse ? "1" : "0");
+}
+
+
 std::vector<PresetComboBox*>& Sidebar::combos_filament()
 {
     return p->combos_filament;
@@ -1842,6 +1858,9 @@ struct Plater::priv
     bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); }
     void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); }
 
+    bool is_sidebar_collapsed() const   { return sidebar->is_collapsed(); }
+    void collapse_sidebur(bool show)    { sidebar->collapse(show); }
+
     void set_current_canvas_as_dirty();
     GLCanvas3D* get_current_canvas3D();
 
@@ -2219,6 +2238,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
 
     // Initialize the Undo / Redo stack with a first snapshot.
     this->take_snapshot(_(L("New Project")));
+
+    // collapse sidebar according to saved value
+    sidebar->collapse(wxGetApp().app_config->get("collapsed_sidebar") == "1");
 }
 
 Plater::priv::~priv()
@@ -4730,6 +4752,9 @@ bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); }
 bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); }
 void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); }
 
+bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); }
+void Plater::collapse_sidebur(bool show) { p->collapse_sidebur(show); }
+
 void Plater::select_all() { p->select_all(); }
 void Plater::deselect_all() { p->deselect_all(); }
 
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index f737cf59a..287cbf024 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -125,6 +125,8 @@ public:
 	bool                    show_export_removable(bool show) const;
     bool                    is_multifilament();
     void                    update_mode();
+    bool                    is_collapsed();
+    void                    collapse(bool collapse);
 
     std::vector<PresetComboBox*>& combos_filament();
 private:
@@ -173,6 +175,9 @@ public:
     bool are_view3D_labels_shown() const;
     void show_view3D_labels(bool show);
 
+    bool is_sidebar_collapsed() const;
+    void collapse_sidebur(bool show);
+
     // Called after the Preferences dialog is closed and the program settings are saved.
     // Update the UI based on the current preferences.
     void update_ui_from_settings();

From e188893c28e32996330b44cbf8e1bece1b4f5565 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 13 Mar 2020 22:37:18 +0100
Subject: [PATCH 02/55] Start to implement "Search through options"

---
 src/slic3r/CMakeLists.txt         |   2 +
 src/slic3r/GUI/Plater.cpp         |  30 ++++-
 src/slic3r/GUI/Plater.hpp         |   1 +
 src/slic3r/GUI/SearchComboBox.cpp | 211 ++++++++++++++++++++++++++++++
 src/slic3r/GUI/SearchComboBox.hpp |  87 ++++++++++++
 src/slic3r/GUI/Tab.cpp            |  67 +++++++++-
 src/slic3r/GUI/Tab.hpp            |   4 +
 7 files changed, 397 insertions(+), 5 deletions(-)
 create mode 100644 src/slic3r/GUI/SearchComboBox.cpp
 create mode 100644 src/slic3r/GUI/SearchComboBox.hpp

diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 5e0c34da0..d6850ee54 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -150,6 +150,8 @@ set(SLIC3R_GUI_SOURCES
     GUI/DoubleSlider.hpp
     GUI/ObjectDataViewModel.cpp
     GUI/ObjectDataViewModel.hpp
+    GUI/SearchComboBox.cpp
+    GUI/SearchComboBox.hpp
     Utils/Http.cpp
     Utils/Http.hpp
     Utils/FixModelByWin10.cpp
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 2a25762a8..66ba11887 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -82,6 +82,7 @@
 #include "../Utils/UndoRedo.hpp"
 #include "../Utils/Thread.hpp"
 #include "RemovableDriveManager.hpp"
+#include "SearchComboBox.hpp"
 
 #include <wx/glcanvas.h>    // Needs to be last because reasons :-/
 #include "WipeTowerDialog.hpp"
@@ -705,6 +706,7 @@ struct Sidebar::priv
 
     wxBoxSizer *sizer_params;
     FreqChangedParams   *frequently_changed_parameters{ nullptr };
+    SearchComboBox      *search_cb{ nullptr };
     ObjectList          *object_list{ nullptr };
     ObjectManipulation  *object_manipulation{ nullptr };
     ObjectSettings      *object_settings{ nullptr };
@@ -831,6 +833,10 @@ Sidebar::Sidebar(Plater *parent)
     p->frequently_changed_parameters = new FreqChangedParams(p->scrolled);
     p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
 
+    // Search combobox
+    p->search_cb = new SearchComboBox(p->scrolled);
+    p->sizer_params->Add(p->search_cb, 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
+
     // Object List
     p->object_list = new ObjectList(p->scrolled);
     p->sizer_params->Add(p->object_list->get_sizer(), 1, wxEXPAND);
@@ -1341,6 +1347,23 @@ bool Sidebar::is_multifilament()
     return p->combos_filament.size() > 1;
 }
 
+static std::vector<SearchInput> get_search_inputs(ConfigOptionMode mode)
+{
+    std::vector<SearchInput> ret {};
+
+    auto& tabs_list = wxGetApp().tabs_list;
+    auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
+    for (auto tab : tabs_list)
+        if (tab->supports_printer_technology(print_tech))
+            ret.emplace_back(SearchInput{tab->get_config(), tab->type(), mode});
+
+    return ret;
+}
+
+void Sidebar::update_search_list()
+{
+    p->search_cb->init(get_search_inputs(m_mode));
+}
 
 void Sidebar::update_mode()
 {
@@ -1348,6 +1371,7 @@ void Sidebar::update_mode()
 
     update_reslice_btn_tooltip();
     update_mode_sizer();
+    update_search_list();
 
     wxWindowUpdateLocker noUpdates(this);
 
@@ -3650,13 +3674,17 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
 
     // update plater with new config
     wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config());
+    if (preset_type == Preset::TYPE_PRINTER) {
     /* Settings list can be changed after printer preset changing, so
      * update all settings items for all item had it.
      * Furthermore, Layers editing is implemented only for FFF printers
      * and for SLA presets they should be deleted
      */
-    if (preset_type == Preset::TYPE_PRINTER)
         wxGetApp().obj_list()->update_object_list_by_printer_technology();
+
+        // print technology could be changed, so we should to update a search list
+        sidebar->update_search_list();
+    }
 }
 
 void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index f737cf59a..9fff172a3 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -125,6 +125,7 @@ public:
 	bool                    show_export_removable(bool show) const;
     bool                    is_multifilament();
     void                    update_mode();
+    void                    update_search_list();
 
     std::vector<PresetComboBox*>& combos_filament();
 private:
diff --git a/src/slic3r/GUI/SearchComboBox.cpp b/src/slic3r/GUI/SearchComboBox.cpp
new file mode 100644
index 000000000..b17643bc9
--- /dev/null
+++ b/src/slic3r/GUI/SearchComboBox.cpp
@@ -0,0 +1,211 @@
+#include "SearchComboBox.hpp"
+
+#include <cstddef>
+#include <algorithm>
+#include <numeric>
+#include <vector>
+#include <string>
+#include <regex>
+#include <future>
+#include <boost/algorithm/string.hpp>
+#include <boost/optional.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/log/trivial.hpp>
+
+#include <wx/sizer.h>
+#include <wx/bmpcbox.h>
+#include "libslic3r/PrintConfig.hpp"
+#include "GUI_App.hpp"
+#include "Tab.hpp"
+#include "PresetBundle.hpp"
+
+using boost::optional;
+
+namespace Slic3r {
+namespace GUI {
+
+bool SearchOptions::Option::containes(const wxString& search_) const
+{
+    wxString search = search_.Lower();
+    wxString label_ = label.Lower();
+    wxString category_ = category.Lower();
+
+    return (opt_key.find(into_u8(search)) != std::string::npos ||
+            label_.Find(search) != wxNOT_FOUND ||
+            category_.Find(search) != wxNOT_FOUND);
+/*    */
+
+    auto search_str = into_u8(search);
+    auto pos = opt_key.find(into_u8(search));
+    bool in_opt_key = pos != std::string::npos;
+    bool in_label = label_.Find(search) != wxNOT_FOUND;
+    bool in_category = category_.Find(search) != wxNOT_FOUND;
+
+    if (in_opt_key || in_label || in_category)
+        return true;
+    return false;
+}
+
+
+template<class T>
+void change_opt_key(std::string& opt_key, DynamicPrintConfig* config)
+{
+    T* opt_cur = static_cast<T*>(config->option(opt_key));
+    if (opt_cur->values.size() > 0)
+        opt_key += "#" + std::to_string(0);
+}
+
+void SearchOptions::append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
+{
+    for (std::string opt_key : config->keys())
+    {
+        const ConfigOptionDef& opt = config->def()->options.at(opt_key);
+        if (opt.mode > mode)
+            continue;
+
+        if (type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER)
+            switch (config->option(opt_key)->type())
+            {
+            case coInts:	change_opt_key<ConfigOptionInts		>(opt_key, config);	break;
+            case coBools:	change_opt_key<ConfigOptionBools	>(opt_key, config);	break;
+            case coFloats:	change_opt_key<ConfigOptionFloats	>(opt_key, config);	break;
+            case coStrings:	change_opt_key<ConfigOptionStrings	>(opt_key, config);	break;
+            case coPercents:change_opt_key<ConfigOptionPercents	>(opt_key, config);	break;
+            case coPoints:	change_opt_key<ConfigOptionPoints	>(opt_key, config);	break;
+            default:		break;
+            }
+
+        wxString label;
+        if (!opt.category.empty())
+            label += _(opt.category) + " : ";
+        label += _(opt.full_label.empty() ? opt.label : opt.full_label);
+
+        options.emplace(Option{ opt_key, label, opt.category, type });
+    }
+}
+
+
+SearchComboBox::SearchComboBox(wxWindow *parent) :
+wxBitmapComboBox(parent, wxID_ANY, "", wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1)),
+    em_unit(wxGetApp().em_unit())
+{
+    SetFont(wxGetApp().normal_font());
+    default_search_line = search_line = _(L("Search through options")) + dots;
+    bmp = ScalableBitmap(this, "search");
+
+#ifdef _WIN32
+    // Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that
+    // the index of the item inside CBN_EDITCHANGE may no more be valid.
+//    EnableTextChangedEvents(false);
+#endif /* _WIN32 */
+
+    Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
+        auto selected_item = this->GetSelection();
+        SearchOptions::Option* opt = reinterpret_cast<SearchOptions::Option*>(this->GetClientData(selected_item));
+        wxGetApp().get_tab(opt->type)->activate_option(opt->opt_key, opt->category);
+
+        evt.StopPropagation();
+
+        SuppressUpdate su(this);
+        this->SetValue(search_line);
+    });
+
+    Bind(wxEVT_TEXT, [this](wxCommandEvent &e) {
+        if (prevent_update)
+            return;
+
+        if (this->IsTextEmpty())
+        {
+            return;
+        }
+
+        if (search_line != this->GetValue()) {
+            update_combobox();
+            search_line = this->GetValue();
+        }
+    }); 
+
+    Bind(wxEVT_KILL_FOCUS, [this](wxEvent & e) {
+        e.Skip();
+
+        SuppressUpdate su(this);        
+        this->SetValue(search_line.IsEmpty() ? default_search_line : search_line);
+    }); 
+}
+
+SearchComboBox::~SearchComboBox()
+{
+}
+
+void SearchComboBox::msw_rescale()
+{
+    em_unit = wxGetApp().em_unit();
+
+    wxSize size = wxSize(25 * em_unit, -1);
+
+    // Set rescaled min height to correct layout
+    this->SetMinSize(size);
+    // Set rescaled size
+    this->SetSize(size);
+
+    update_combobox();
+}
+
+void SearchComboBox::init(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
+{
+    search_list.clear();
+    search_list.append_options(config, type, mode);
+
+    update_combobox();
+}
+
+void SearchComboBox::init(std::vector<SearchInput> input_values)
+{
+    search_list.clear();
+
+    for (auto i : input_values)
+        search_list.append_options(i.config, i.type, i.mode);
+
+    update_combobox();
+}
+
+void SearchComboBox::update_combobox()
+{
+    wxString search_str = this->GetValue();
+    if (search_str.IsEmpty() || search_str == default_search_line)
+        // add whole options list to the controll
+        append_all_items();
+    else
+        append_items(search_str);
+}
+
+void SearchComboBox::append_all_items()
+{
+    this->Clear();
+    for (const SearchOptions::Option& item : search_list.options)
+        if (!item.label.IsEmpty())
+            append(item.label, (void*)&item);
+
+    SuppressUpdate su(this);
+    this->SetValue(default_search_line);
+}
+
+void SearchComboBox::append_items(const wxString& search)
+{
+    this->Clear();
+
+    auto cmp = [](SearchOptions::Option* o1, SearchOptions::Option* o2) { return o1->label > o2->label; };
+    std::set<SearchOptions::Option*, decltype(cmp)> ret(cmp);
+
+    for (const SearchOptions::Option& option : search_list.options)
+        if (option.containes(search))
+            append(option.label, (void*)&option);
+
+    this->Popup();
+    SuppressUpdate su(this);
+    this->SetValue(search);
+    this->SetInsertionPointEnd();
+}
+
+}}    // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/SearchComboBox.hpp b/src/slic3r/GUI/SearchComboBox.hpp
new file mode 100644
index 000000000..0f8e83137
--- /dev/null
+++ b/src/slic3r/GUI/SearchComboBox.hpp
@@ -0,0 +1,87 @@
+#ifndef slic3r_SearchComboBox_hpp_
+#define slic3r_SearchComboBox_hpp_
+
+#include <memory>
+#include <vector>
+#include <boost/filesystem/path.hpp>
+
+#include <wx/bmpcbox.h>
+
+#include "Preset.hpp"
+#include "wxExtensions.hpp"
+
+
+namespace Slic3r {
+
+namespace GUI {
+
+struct SearchInput
+{
+    DynamicPrintConfig* config  {nullptr};
+    Preset::Type        type    {Preset::TYPE_INVALID};
+    ConfigOptionMode    mode    {comSimple};
+};
+
+class SearchOptions
+{
+public:
+    struct Option {
+        bool operator<(const Option& other) const { return other.label > this->label; }
+        bool operator>(const Option& other) const { return other.label < this->label; }
+
+        std::string     opt_key;
+        wxString        label;
+        wxString        category;
+        Preset::Type    type {Preset::TYPE_INVALID};
+        // wxString     grope;
+
+        bool containes(const wxString& search) const;
+    };
+
+    std::set<Option> options {};
+
+    void clear() { options. clear(); }
+    void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
+};
+
+class SearchComboBox : public wxBitmapComboBox
+{
+    class SuppressUpdate
+    {
+        SearchComboBox* m_cb;
+    public:
+        SuppressUpdate(SearchComboBox* cb) : 
+                m_cb(cb)    { m_cb->prevent_update = true ; }
+        ~SuppressUpdate()   { m_cb->prevent_update = false; }
+    };                                                 
+
+public:
+    SearchComboBox(wxWindow *parent);
+    ~SearchComboBox();
+
+    int     append(const wxString& item, void* clientData)          { return Append(item, bmp.bmp(), clientData); }
+    int     append(const wxString& item, wxClientData* clientData)  { return Append(item, bmp.bmp(), clientData); }
+    
+    void    append_all_items();
+    void    append_items(const wxString& search);
+
+    void    msw_rescale();
+
+    void	init(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
+    void    init(std::vector<SearchInput> input_values);
+    void    update_combobox();
+
+private:
+    SearchOptions		search_list;
+    wxString            default_search_line;
+    wxString            search_line;
+
+    int                 em_unit;
+    bool                prevent_update {false};
+
+    ScalableBitmap      bmp;
+};
+
+}}
+
+#endif //slic3r_SearchComboBox_hpp_
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 9e49dc5bd..ec4aab847 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -114,6 +114,9 @@ void Tab::create_preset_tab()
     // preset chooser
     m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1));
 
+    // search combox
+    m_search_cb = new SearchComboBox(panel);
+
     auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
     //buttons
@@ -186,13 +189,18 @@ void Tab::create_preset_tab()
     m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->AddSpacer(int(4 * scale_factor));
     m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->AddSpacer(int(16 * scale_factor));
+    m_hsizer->AddSpacer(int(/*16*/8 * scale_factor));
     m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->AddSpacer(int(64 * scale_factor));
+    m_hsizer->AddSpacer(int(8 * scale_factor));
+    m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
+    m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
     m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->AddSpacer(int(32 * scale_factor));
-    m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
+    m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
+    m_hsizer->Add(m_search_cb, 0, wxALIGN_CENTER_VERTICAL);
+    m_hsizer->AddSpacer(int(16 * scale_factor));
+//    m_hsizer->AddSpacer(int(32 * scale_factor));
+//    m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
     // m_hsizer->AddStretchSpacer(32);
     // StretchSpacer has a strange behavior under OSX, so
     // There is used just additional sizer for m_mode_sizer with right alignment
@@ -752,6 +760,9 @@ void Tab::update_mode()
     update_visibility();
 
     update_changed_tree_ui();
+
+    // update list of options for search
+    m_search_cb->init(m_config, type(), m_mode);
 }
 
 void Tab::update_visibility()
@@ -778,6 +789,7 @@ void Tab::msw_rescale()
     m_em_unit = wxGetApp().em_unit();
 
     m_mode_sizer->msw_rescale();
+    m_search_cb->msw_rescale();
 
     m_presets_choice->SetSize(35 * m_em_unit, -1);
     m_treectrl->SetMinSize(wxSize(20 * m_em_unit, -1));
@@ -820,6 +832,19 @@ Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/
     return field;
 }
 
+Field* Tab::get_field(const t_config_option_key& opt_key, Page** selected_page, int opt_index/* = -1*/)
+{
+    Field* field = nullptr;
+    for (auto page : m_pages) {
+        field = page->get_field(opt_key, opt_index);
+        if (field != nullptr) {
+            *selected_page = page.get();
+            return field;
+        }
+    }
+    return field;
+}
+
 // Set a key/value pair on this page. Return true if the value has been modified.
 // Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer
 // after a preset is loaded.
@@ -925,6 +950,40 @@ void Tab::update_wiping_button_visibility() {
     }
 }
 
+void Tab::activate_option(const std::string& opt_key, const wxString& category)
+{
+    Page* page {nullptr};
+    Field* field = get_field(opt_key, &page);
+
+    // for option, which doesn't have field but just a text or button
+    wxString page_title = (!field || !page) ? category : page->title();
+
+    auto cur_item = m_treectrl->GetFirstVisibleItem();
+    if (!cur_item || !m_treectrl->IsVisible(cur_item))
+        return;
+
+    while (cur_item) {
+        auto title = m_treectrl->GetItemText(cur_item);
+        if (page_title != title) {
+            cur_item = m_treectrl->GetNextVisible(cur_item);
+            continue;
+        }
+
+        m_treectrl->SelectItem(cur_item);
+        break;
+    }
+
+    // we should to activate a tab with searched option, if it doesn't.
+    wxNotebook* tap_panel = wxGetApp().tab_panel();
+    int page_id = tap_panel->FindPage(this);
+    if (tap_panel->GetSelection() != page_id)
+        tap_panel->SetSelection(page_id);
+
+    // focused selected field
+    if (field)
+        field->getWindow()->SetFocus();
+}
+
 
 // Call a callback to update the selection of presets on the plater:
 // To update the content of the selection boxes,
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index e2b00dc40..8805d8d9e 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -33,6 +33,7 @@
 #include "Event.hpp"
 #include "wxExtensions.hpp"
 #include "ConfigManipulation.hpp"
+#include "SearchComboBox.hpp"
 
 namespace Slic3r {
 namespace GUI {
@@ -121,6 +122,7 @@ protected:
 	std::string			m_name;
 	const wxString		m_title;
 	PresetBitmapComboBox*	m_presets_choice;
+	SearchComboBox*		m_search_cb;
 	ScalableButton*		m_btn_save_preset;
 	ScalableButton*		m_btn_delete_preset;
 	ScalableButton*		m_btn_hide_incompatible_presets;
@@ -299,6 +301,7 @@ public:
     void            update_visibility();
     virtual void    msw_rescale();
 	Field*			get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
+    Field*          get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
 	bool			set_value(const t_config_option_key& opt_key, const boost::any& value);
 	wxSizer*		description_line_widget(wxWindow* parent, ogStaticText** StaticText);
 	bool			current_preset_is_dirty();
@@ -310,6 +313,7 @@ public:
 	void			on_value_change(const std::string& opt_key, const boost::any& value);
 
     void            update_wiping_button_visibility();
+	void			activate_option(const std::string& opt_key, const wxString& category);
 
 protected:
 	void			create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget);

From 8898dd56189dccb3155a84b214ab8ccb643de03a Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 20 Mar 2020 16:10:00 +0100
Subject: [PATCH 03/55] Added missed icon

---
 resources/icons/search.svg | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 resources/icons/search.svg

diff --git a/resources/icons/search.svg b/resources/icons/search.svg
new file mode 100644
index 000000000..bf97904e8
--- /dev/null
+++ b/resources/icons/search.svg
@@ -0,0 +1 @@
+<svg fill="#808080" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px"><path d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/></svg>
\ No newline at end of file

From ebfaf7abb0429fa9c2bc77ed5367a6924b2cc605 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Sat, 21 Mar 2020 19:45:55 +0100
Subject: [PATCH 04/55] Removed SearchCombobox from the Sidebar.
 Tab->SearchCombobox works with all parameters now. Overridden GetWindow() for
 PointCtrl.

---
 src/slic3r/GUI/Field.hpp          |  1 +
 src/slic3r/GUI/Plater.cpp         | 20 +++++++++++++++++---
 src/slic3r/GUI/SearchComboBox.cpp | 14 ++++----------
 src/slic3r/GUI/Tab.cpp            |  2 +-
 src/slic3r/GUI/Tab.hpp            |  1 +
 5 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp
index 1f8b642b1..03fa28b2b 100644
--- a/src/slic3r/GUI/Field.hpp
+++ b/src/slic3r/GUI/Field.hpp
@@ -460,6 +460,7 @@ public:
 		x_textctrl->Disable();
 		y_textctrl->Disable(); }
 	wxSizer*		getSizer() override { return sizer; }
+	wxWindow*		getWindow() override { return dynamic_cast<wxWindow*>(x_textctrl); }
 };
 
 class StaticText : public Field {
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 5a1fa7487..ab303b7b0 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -834,8 +834,8 @@ Sidebar::Sidebar(Plater *parent)
     p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
 
     // Search combobox
-    p->search_cb = new SearchComboBox(p->scrolled);
-    p->sizer_params->Add(p->search_cb, 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
+//    p->search_cb = new SearchComboBox(p->scrolled);
+//    p->sizer_params->Add(p->search_cb, 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
 
     // Object List
     p->object_list = new ObjectList(p->scrolled);
@@ -1362,7 +1362,21 @@ static std::vector<SearchInput> get_search_inputs(ConfigOptionMode mode)
 
 void Sidebar::update_search_list()
 {
-    p->search_cb->init(get_search_inputs(m_mode));
+    if (p->search_cb)
+        p->search_cb->init(get_search_inputs(m_mode));
+
+    std::vector<SearchInput> search_list{};
+
+    auto& tabs_list = wxGetApp().tabs_list;
+    auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
+
+    for (auto tab : tabs_list)
+        if (tab->supports_printer_technology(print_tech))
+            search_list.emplace_back(SearchInput{ tab->get_config(), tab->type(), m_mode });
+
+    for (auto tab : tabs_list)
+        if (tab->supports_printer_technology(print_tech))
+            tab->get_search_cb()->init(search_list);
 }
 
 void Sidebar::update_mode()
diff --git a/src/slic3r/GUI/SearchComboBox.cpp b/src/slic3r/GUI/SearchComboBox.cpp
index b17643bc9..11742f25c 100644
--- a/src/slic3r/GUI/SearchComboBox.cpp
+++ b/src/slic3r/GUI/SearchComboBox.cpp
@@ -34,7 +34,6 @@ bool SearchOptions::Option::containes(const wxString& search_) const
     return (opt_key.find(into_u8(search)) != std::string::npos ||
             label_.Find(search) != wxNOT_FOUND ||
             category_.Find(search) != wxNOT_FOUND);
-/*    */
 
     auto search_str = into_u8(search);
     auto pos = opt_key.find(into_u8(search));
@@ -87,11 +86,11 @@ void SearchOptions::append_options(DynamicPrintConfig* config, Preset::Type type
 
 
 SearchComboBox::SearchComboBox(wxWindow *parent) :
-wxBitmapComboBox(parent, wxID_ANY, "", wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1)),
+wxBitmapComboBox(parent, wxID_ANY, _(L("Type here to search")) + dots, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1)),
     em_unit(wxGetApp().em_unit())
 {
     SetFont(wxGetApp().normal_font());
-    default_search_line = search_line = _(L("Search through options")) + dots;
+    default_search_line = search_line = _(L("Type here to search")) + dots;
     bmp = ScalableBitmap(this, "search");
 
 #ifdef _WIN32
@@ -124,14 +123,9 @@ wxBitmapComboBox(parent, wxID_ANY, "", wxDefaultPosition, wxSize(25 * wxGetApp()
             update_combobox();
             search_line = this->GetValue();
         }
-    }); 
 
-    Bind(wxEVT_KILL_FOCUS, [this](wxEvent & e) {
         e.Skip();
-
-        SuppressUpdate su(this);        
-        this->SetValue(search_line.IsEmpty() ? default_search_line : search_line);
-    }); 
+    });
 }
 
 SearchComboBox::~SearchComboBox()
@@ -202,7 +196,7 @@ void SearchComboBox::append_items(const wxString& search)
         if (option.containes(search))
             append(option.label, (void*)&option);
 
-    this->Popup();
+//    this->Popup();
     SuppressUpdate su(this);
     this->SetValue(search);
     this->SetInsertionPointEnd();
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 919324956..f9501069d 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -762,7 +762,7 @@ void Tab::update_mode()
     update_changed_tree_ui();
 
     // update list of options for search
-    m_search_cb->init(m_config, type(), m_mode);
+//    m_search_cb->init(m_config, type(), m_mode);
 }
 
 void Tab::update_visibility()
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 8805d8d9e..705a806a3 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -308,6 +308,7 @@ public:
 
 	DynamicPrintConfig*	get_config() { return m_config; }
 	PresetCollection*	get_presets() { return m_presets; }
+	SearchComboBox*     get_search_cb() { return m_search_cb; }
 	size_t				get_selected_preset_item() { return m_selected_preset_item; }
 
 	void			on_value_change(const std::string& opt_key, const boost::any& value);

From 1010fff8af3af713f5eb80f308fd84f91bf7d73b Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 25 Mar 2020 10:24:43 +0100
Subject: [PATCH 05/55] Added fts_fuzzy_match.h borrowed from
 https://github.com/forrestthewoods/lib_fts

Search impoved using lib_fts
Function for filtering by score is prepared.
---
 src/slic3r/GUI/AboutDialog.cpp    |   4 +-
 src/slic3r/GUI/SearchComboBox.cpp |  61 ++++++---
 src/slic3r/GUI/SearchComboBox.hpp |  23 +++-
 src/slic3r/GUI/fts_fuzzy_match.h  | 221 ++++++++++++++++++++++++++++++
 4 files changed, 283 insertions(+), 26 deletions(-)
 create mode 100644 src/slic3r/GUI/fts_fuzzy_match.h

diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp
index 98b04a63d..0607c1b01 100644
--- a/src/slic3r/GUI/AboutDialog.cpp
+++ b/src/slic3r/GUI/AboutDialog.cpp
@@ -114,7 +114,9 @@ void CopyrightsDialog::fill_entries()
         { "Icons for STL and GCODE files."
                             , "Akira Yasuda"                                , "http://3dp0.com/icons-for-stl-and-gcode/" },
         { "AppImage packaging for Linux using AppImageKit"
-                            , "2004-2019 Simon Peter and contributors"      , "https://appimage.org/" }
+                            , "2004-2019 Simon Peter and contributors"      , "https://appimage.org/" },
+        { "lib_fts"
+                            , "Forrest Smith"                               , "https://www.forrestthewoods.com/" }
     };
 }
 
diff --git a/src/slic3r/GUI/SearchComboBox.cpp b/src/slic3r/GUI/SearchComboBox.cpp
index 11742f25c..a2efd5b02 100644
--- a/src/slic3r/GUI/SearchComboBox.cpp
+++ b/src/slic3r/GUI/SearchComboBox.cpp
@@ -20,6 +20,9 @@
 #include "Tab.hpp"
 #include "PresetBundle.hpp"
 
+#define FTS_FUZZY_MATCH_IMPLEMENTATION
+#include "fts_fuzzy_match.h"
+
 using boost::optional;
 
 namespace Slic3r {
@@ -27,23 +30,22 @@ namespace GUI {
 
 bool SearchOptions::Option::containes(const wxString& search_) const
 {
-    wxString search = search_.Lower();
-    wxString label_ = label.Lower();
-    wxString category_ = category.Lower();
+    char const* search_pattern = search_.utf8_str();
+    char const* opt_key_str    = opt_key.c_str();
+    char const* label_str      = label.utf8_str();
 
-    return (opt_key.find(into_u8(search)) != std::string::npos ||
-            label_.Find(search) != wxNOT_FOUND ||
-            category_.Find(search) != wxNOT_FOUND);
+    return  fts::fuzzy_match_simple(search_pattern, label_str   )   ||
+            fts::fuzzy_match_simple(search_pattern, opt_key_str )   ; 
+}
 
-    auto search_str = into_u8(search);
-    auto pos = opt_key.find(into_u8(search));
-    bool in_opt_key = pos != std::string::npos;
-    bool in_label = label_.Find(search) != wxNOT_FOUND;
-    bool in_category = category_.Find(search) != wxNOT_FOUND;
+bool SearchOptions::Option::is_matched_option(const wxString& search, int& outScore)
+{
+    char const* search_pattern = search.utf8_str();
+    char const* opt_key_str    = opt_key.c_str();
+    char const* label_str      = label.utf8_str();
 
-    if (in_opt_key || in_label || in_category)
-        return true;
-    return false;
+    return (fts::fuzzy_match(search_pattern, label_str   , outScore)   ||
+            fts::fuzzy_match(search_pattern, opt_key_str , outScore)   ); 
 }
 
 
@@ -80,10 +82,20 @@ void SearchOptions::append_options(DynamicPrintConfig* config, Preset::Type type
             label += _(opt.category) + " : ";
         label += _(opt.full_label.empty() ? opt.label : opt.full_label);
 
-        options.emplace(Option{ opt_key, label, opt.category, type });
+        options.emplace_back(Option{ label, opt_key, opt.category, type });
     }
 }
 
+void SearchOptions::apply_filters(const wxString& search)
+{
+    clear_filters();
+    for (auto option : options) {
+        int score;
+        if (option.is_matched_option(search, score))
+            filters.emplace_back(Filter{ option.label, score });
+    }
+    sort_filters();
+}
 
 SearchComboBox::SearchComboBox(wxWindow *parent) :
 wxBitmapComboBox(parent, wxID_ANY, _(L("Type here to search")) + dots, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1)),
@@ -148,18 +160,19 @@ void SearchComboBox::msw_rescale()
 
 void SearchComboBox::init(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
 {
-    search_list.clear();
+    search_list.clear_options();
     search_list.append_options(config, type, mode);
+    search_list.sort_options();
 
     update_combobox();
 }
 
 void SearchComboBox::init(std::vector<SearchInput> input_values)
 {
-    search_list.clear();
-
+    search_list.clear_options();
     for (auto i : input_values)
         search_list.append_options(i.config, i.type, i.mode);
+    search_list.sort_options();
 
     update_combobox();
 }
@@ -188,15 +201,19 @@ void SearchComboBox::append_all_items()
 void SearchComboBox::append_items(const wxString& search)
 {
     this->Clear();
-
-    auto cmp = [](SearchOptions::Option* o1, SearchOptions::Option* o2) { return o1->label > o2->label; };
-    std::set<SearchOptions::Option*, decltype(cmp)> ret(cmp);
+/*
+    search_list.apply_filters(search);
+    for (auto filter : search_list.filters) {
+        auto it = std::lower_bound(search_list.options.begin(), search_list.options.end(), SearchOptions::Option{filter.label});
+        if (it != search_list.options.end())
+            append(it->label, (void*)(&(*it)));
+    }
+*/
 
     for (const SearchOptions::Option& option : search_list.options)
         if (option.containes(search))
             append(option.label, (void*)&option);
 
-//    this->Popup();
     SuppressUpdate su(this);
     this->SetValue(search);
     this->SetInsertionPointEnd();
diff --git a/src/slic3r/GUI/SearchComboBox.hpp b/src/slic3r/GUI/SearchComboBox.hpp
index 0f8e83137..482bb18eb 100644
--- a/src/slic3r/GUI/SearchComboBox.hpp
+++ b/src/slic3r/GUI/SearchComboBox.hpp
@@ -29,19 +29,36 @@ public:
         bool operator<(const Option& other) const { return other.label > this->label; }
         bool operator>(const Option& other) const { return other.label < this->label; }
 
-        std::string     opt_key;
         wxString        label;
+        std::string     opt_key;
         wxString        category;
         Preset::Type    type {Preset::TYPE_INVALID};
         // wxString     grope;
 
         bool containes(const wxString& search) const;
+        bool is_matched_option(const wxString &search, int &outScore);
     };
+    std::vector<Option> options {};
 
-    std::set<Option> options {};
+    struct Filter {
+        wxString        label;
+        int             outScore {0};
+    };
+    std::vector<Filter> filters {};
 
-    void clear() { options. clear(); }
+    void clear_options() { options.clear(); }
+    void clear_filters() { filters.clear(); }
     void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
+    void apply_filters(const wxString& search);
+
+    void sort_options() {
+        std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) {
+            return o1.label < o2.label; });
+    }
+    void sort_filters() {
+        std::sort(filters.begin(), filters.end(), [](const Filter& f1, const Filter& f2) {
+            return f1.outScore > f2.outScore; });
+    };
 };
 
 class SearchComboBox : public wxBitmapComboBox
diff --git a/src/slic3r/GUI/fts_fuzzy_match.h b/src/slic3r/GUI/fts_fuzzy_match.h
new file mode 100644
index 000000000..046027e16
--- /dev/null
+++ b/src/slic3r/GUI/fts_fuzzy_match.h
@@ -0,0 +1,221 @@
+  // LICENSE
+//
+//   This software is dual-licensed to the public domain and under the following
+//   license: you are granted a perpetual, irrevocable license to copy, modify,
+//   publish, and distribute this file as you see fit.
+//
+// VERSION 
+//   0.2.0  (2017-02-18)  Scored matches perform exhaustive search for best score
+//   0.1.0  (2016-03-28)  Initial release
+//
+// AUTHOR
+//   Forrest Smith
+//
+// NOTES
+//   Compiling
+//     You MUST add '#define FTS_FUZZY_MATCH_IMPLEMENTATION' before including this header in ONE source file to create implementation.
+//
+//   fuzzy_match_simple(...)
+//     Returns true if each character in pattern is found sequentially within str
+//
+//   fuzzy_match(...)
+//     Returns true if pattern is found AND calculates a score.
+//     Performs exhaustive search via recursion to find all possible matches and match with highest score.
+//     Scores values have no intrinsic meaning. Possible score range is not normalized and varies with pattern.
+//     Recursion is limited internally (default=10) to prevent degenerate cases (pattern="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+//     Uses uint8_t for match indices. Therefore patterns are limited to 256 characters.
+//     Score system should be tuned for YOUR use case. Words, sentences, file names, or method names all prefer different tuning.
+
+
+#ifndef FTS_FUZZY_MATCH_H
+#define FTS_FUZZY_MATCH_H
+
+
+#include <cstdint> // uint8_t
+#include <ctype.h> // ::tolower, ::toupper
+#include <cstring> // memcpy
+
+#include <cstdio>
+
+// Public interface
+namespace fts {
+    static bool fuzzy_match_simple(char const * pattern, char const * str);
+    static bool fuzzy_match(char const * pattern, char const * str, int & outScore);
+    static bool fuzzy_match(char const * pattern, char const * str, int & outScore, uint8_t * matches, int maxMatches);
+}
+
+
+#ifdef FTS_FUZZY_MATCH_IMPLEMENTATION
+namespace fts {
+
+    // Forward declarations for "private" implementation
+    namespace fuzzy_internal {
+        static bool fuzzy_match_recursive(const char * pattern, const char * str, int & outScore, const char * strBegin,          
+            uint8_t const * srcMatches,  uint8_t * newMatches,  int maxMatches, int nextMatch, 
+            int & recursionCount, int recursionLimit);
+    }
+
+    // Public interface
+    static bool fuzzy_match_simple(char const * pattern, char const * str) {
+        while (*pattern != '\0' && *str != '\0')  {
+            if (tolower(*pattern) == tolower(*str))
+                ++pattern;
+            ++str;
+        }
+
+        return *pattern == '\0' ? true : false;
+    }
+
+    static bool fuzzy_match(char const * pattern, char const * str, int & outScore) {
+        
+        uint8_t matches[256];
+        return fuzzy_match(pattern, str, outScore, matches, sizeof(matches));
+    }
+
+    static bool fuzzy_match(char const * pattern, char const * str, int & outScore, uint8_t * matches, int maxMatches) {
+        int recursionCount = 0;
+        int recursionLimit = 10;
+
+        return fuzzy_internal::fuzzy_match_recursive(pattern, str, outScore, str, nullptr, matches, maxMatches, 0, recursionCount, recursionLimit);
+    }
+
+    // Private implementation
+    static bool fuzzy_internal::fuzzy_match_recursive(const char * pattern, const char * str, int & outScore, 
+        const char * strBegin, uint8_t const * srcMatches, uint8_t * matches, int maxMatches, 
+        int nextMatch, int & recursionCount, int recursionLimit)
+    {
+        // Count recursions
+        ++recursionCount;
+        if (recursionCount >= recursionLimit)
+            return false;
+
+        // Detect end of strings
+        if (*pattern == '\0' || *str == '\0')
+            return false;
+
+        // Recursion params
+        bool recursiveMatch = false;
+        uint8_t bestRecursiveMatches[256];
+        int bestRecursiveScore = 0;
+
+        // Loop through pattern and str looking for a match
+        bool first_match = true;
+        while (*pattern != '\0' && *str != '\0') {
+            
+            // Found match
+            if (tolower(*pattern) == tolower(*str)) {
+
+                // Supplied matches buffer was too short
+                if (nextMatch >= maxMatches)
+                    return false;
+                
+                // "Copy-on-Write" srcMatches into matches
+                if (first_match && srcMatches) {
+                    memcpy(matches, srcMatches, nextMatch);
+                    first_match = false;
+                }
+
+                // Recursive call that "skips" this match
+                uint8_t recursiveMatches[256];
+                int recursiveScore;
+                if (fuzzy_match_recursive(pattern, str + 1, recursiveScore, strBegin, matches, recursiveMatches, sizeof(recursiveMatches), nextMatch, recursionCount, recursionLimit)) {
+                    
+                    // Pick best recursive score
+                    if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
+                        memcpy(bestRecursiveMatches, recursiveMatches, 256);
+                        bestRecursiveScore = recursiveScore;
+                    }
+                    recursiveMatch = true;
+                }
+
+                // Advance
+                matches[nextMatch++] = (uint8_t)(str - strBegin);
+                ++pattern;
+            }
+            ++str;
+        }
+
+        // Determine if full pattern was matched
+        bool matched = *pattern == '\0' ? true : false;
+
+        // Calculate score
+        if (matched) {
+            const int sequential_bonus = 15;            // bonus for adjacent matches
+            const int separator_bonus = 30;             // bonus if match occurs after a separator
+            const int camel_bonus = 30;                 // bonus if match is uppercase and prev is lower
+            const int first_letter_bonus = 15;          // bonus if the first letter is matched
+
+            const int leading_letter_penalty = -5;      // penalty applied for every letter in str before the first match
+            const int max_leading_letter_penalty = -15; // maximum penalty for leading letters
+            const int unmatched_letter_penalty = -1;    // penalty for every letter that doesn't matter
+
+            // Iterate str to end
+            while (*str != '\0')
+                ++str;
+
+            // Initialize score
+            outScore = 100;
+
+            // Apply leading letter penalty
+            int penalty = leading_letter_penalty * matches[0];
+            if (penalty < max_leading_letter_penalty)
+                penalty = max_leading_letter_penalty;
+            outScore += penalty;
+
+            // Apply unmatched penalty
+            int unmatched = (int)(str - strBegin) - nextMatch;
+            outScore += unmatched_letter_penalty * unmatched;
+
+            // Apply ordering bonuses
+            for (int i = 0; i < nextMatch; ++i) {
+                uint8_t currIdx = matches[i];
+
+                if (i > 0) {
+                    uint8_t prevIdx = matches[i - 1];
+
+                    // Sequential
+                    if (currIdx == (prevIdx + 1))
+                        outScore += sequential_bonus;
+                }
+
+                // Check for bonuses based on neighbor character value
+                if (currIdx > 0) {
+                    // Camel case
+                    char neighbor = strBegin[currIdx - 1];
+                    char curr = strBegin[currIdx];
+                    if (::islower(neighbor) && ::isupper(curr))
+                        outScore += camel_bonus;
+
+                    // Separator
+                    bool neighborSeparator = neighbor == '_' || neighbor == ' ';
+                    if (neighborSeparator)
+                        outScore += separator_bonus;
+                }
+                else {
+                    // First letter
+                    outScore += first_letter_bonus;
+                }
+            }
+        }
+
+        // Return best result
+        if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
+            // Recursive score is better than "this"
+            memcpy(matches, bestRecursiveMatches, maxMatches);
+            outScore = bestRecursiveScore;
+            return true;
+        }
+        else if (matched) {
+            // "this" score is better than recursive
+            return true;
+        }
+        else {
+            // no match
+            return false;
+        }
+    }
+} // namespace fts
+
+#endif // FTS_FUZZY_MATCH_IMPLEMENTATION
+
+#endif // FTS_FUZZY_MATCH_H

From 14703fe56128c94215cdeeb927119337918eb05a Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Sat, 28 Mar 2020 19:39:24 +0100
Subject: [PATCH 06/55] Implemented Search on Plater, usint imGui

---
 resources/icons/search_.svg       |  4 ++
 src/slic3r/GUI/GLCanvas3D.cpp     | 79 +++++++++++++++++++++++++++++++
 src/slic3r/GUI/GLCanvas3D.hpp     |  2 +
 src/slic3r/GUI/ImGuiWrapper.cpp   | 55 +++++++++++++++++++++
 src/slic3r/GUI/ImGuiWrapper.hpp   |  1 +
 src/slic3r/GUI/Plater.cpp         | 53 +++++++++++++++------
 src/slic3r/GUI/Plater.hpp         |  9 +++-
 src/slic3r/GUI/SearchComboBox.cpp | 46 +++++++++++++++---
 src/slic3r/GUI/SearchComboBox.hpp | 15 +++++-
 9 files changed, 241 insertions(+), 23 deletions(-)
 create mode 100644 resources/icons/search_.svg

diff --git a/resources/icons/search_.svg b/resources/icons/search_.svg
new file mode 100644
index 000000000..679bb30f7
--- /dev/null
+++ b/resources/icons/search_.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px">
+<path fill="#FFFFFF" d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
+<path fill="#ED6B21" d="M 13.261719 14.867188 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
+</svg>
\ No newline at end of file
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index a02b2de69..3cd1319b5 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1450,6 +1450,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
+wxDEFINE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
 wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
 wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
@@ -4205,6 +4206,55 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const
     return action_taken;
 }
 
+// Getter for the const char*[] for the search list 
+static bool search_string_getter(int idx, const char** out_text)
+{
+    return wxGetApp().plater()->search_string_getter(idx, out_text);
+}
+
+bool GLCanvas3D::_render_search_list(float pos_x) const
+{
+    bool action_taken = false;
+    ImGuiWrapper* imgui = wxGetApp().imgui();
+
+    const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
+    imgui->set_next_window_pos(x, m_undoredo_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
+    std::string title = L("Search");
+    imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
+
+    size_t selected = size_t(-1);
+    bool edited = false;
+    float em = static_cast<float>(wxGetApp().em_unit());
+#if ENABLE_RETINA_GL
+	em *= m_retina_helper->get_scale_factor();
+#endif
+
+    std::string& search_line = wxGetApp().sidebar().get_search_line();
+    char *s = new char[255];
+    strcpy(s, search_line.empty() ? _utf8(L("Type here to search")).c_str() : search_line.c_str());
+
+    imgui->search_list(ImVec2(22 * em, 30 * em), &search_string_getter, s, selected, edited);
+
+    search_line = s;
+    delete [] s;
+
+    if (selected != size_t(-1))
+    {
+        wxGetApp().sidebar().jump_to_option(selected);
+        action_taken = true;
+    }
+
+    if (edited)
+    {
+        wxGetApp().sidebar().apply_search_filter();
+        action_taken = true;
+    }
+
+    imgui->end();
+
+    return action_taken;
+}
+
 #define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0
 #if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
 static void debug_output_thumbnail(const ThumbnailData& thumbnail_data)
@@ -4723,6 +4773,23 @@ bool GLCanvas3D::_init_main_toolbar()
     if (!m_main_toolbar.add_item(item))
         return false;
 
+    if (!m_main_toolbar.add_separator())
+        return false;
+
+    item.name = "search";
+    item.icon_filename = "search_.svg";
+    item.tooltip = _utf8(L("Search"));
+    item.sprite_id = 11;
+    item.left.render_callback = [this](float left, float right, float, float) {
+        if (m_canvas != nullptr)
+        {
+            _render_search_list(0.5f * (left + right));
+        }
+    };
+    item.enabling_callback = []()->bool { return true; };
+    if (!m_main_toolbar.add_item(item))
+        return false;
+
     return true;
 }
 
@@ -4829,6 +4896,18 @@ bool GLCanvas3D::_init_undoredo_toolbar()
         return can_redo;
     };
 
+    if (!m_undoredo_toolbar.add_item(item))
+        return false;
+
+    if (!m_undoredo_toolbar.add_separator())
+        return false;
+
+    item.name = "collapse_sidebar";
+    item.icon_filename = "cross.svg";
+    item.tooltip = _utf8(L("Collapse right panel"));
+    item.sprite_id = 2;
+    item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR)); };
+    item.enabling_callback = []()->bool { return true; };
     if (!m_undoredo_toolbar.add_item(item))
         return false;
 
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index c50935b87..15249b1f2 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -104,6 +104,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
+wxDECLARE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
 wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
 wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
@@ -725,6 +726,7 @@ private:
     void _render_sla_slices() const;
     void _render_selection_sidebar_hints() const;
     bool _render_undo_redo_stack(const bool is_undo, float pos_x) const;
+    bool _render_search_list(float pos_x) const;
     void _render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
     // render thumbnail using an off-screen framebuffer
     void _render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index a44e843b8..00b800042 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -400,6 +400,61 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (
     return is_hovered;
 }
 
+void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char**), char* search_str, size_t& selected, bool& edited)
+{
+    // ImGui::ListBoxHeader("", size);
+    {   
+        // rewrote part of function to add a TextInput instead of label Text
+        ImGuiContext& g = *GImGui;
+        ImGuiWindow* window = ImGui::GetCurrentWindow();
+        if (window->SkipItems)
+            return ;
+
+        const ImGuiStyle& style = g.Style;
+
+        // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
+        ImVec2 size = ImGui::CalcItemSize(size_, ImGui::CalcItemWidth(), ImGui::GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
+        ImRect frame_bb(window->DC.CursorPos, ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y + size.y));
+
+        ImRect bb(frame_bb.Min, frame_bb.Max);
+        window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy.
+        g.NextItemData.ClearFlags();
+
+        if (!ImGui::IsRectVisible(bb.Min, bb.Max))
+        {
+            ImGui::ItemSize(bb.GetSize(), style.FramePadding.y);
+            ImGui::ItemAdd(bb, 0, &frame_bb);
+            return ;
+        }
+
+        ImGui::BeginGroup();
+
+        const ImGuiID id = ImGui::GetID(search_str);
+        ImVec2 search_size = ImVec2(size.x, ImGui::GetTextLineHeightWithSpacing() + style.ItemSpacing.y);
+
+        ImGui::InputTextEx("", NULL, search_str, 20, search_size, 0, NULL, NULL);
+        edited = ImGui::IsItemEdited();
+
+        ImGui::BeginChildFrame(id, frame_bb.GetSize());
+    }
+
+    size_t i = 0;
+    const char* item_text;
+    while (items_getter(i, &item_text))
+    {
+        ImGui::Selectable(item_text, false);
+
+        if (ImGui::IsItemHovered())
+            ImGui::SetTooltip("%s", item_text);
+
+        if (ImGui::IsItemClicked())
+            selected = i;
+        i++;
+    }
+
+    ImGui::ListBoxFooter();
+}
+
 void ImGuiWrapper::disabled_begin(bool disabled)
 {
     wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index 417561881..9001f6b5a 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -74,6 +74,7 @@ public:
     bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
     bool combo(const wxString& label, const std::vector<std::string>& options, int& selection);   // Use -1 to not mark any option as selected
     bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
+    void search_list(const ImVec2& size, bool (*items_getter)(int, const char**), char* search_str, size_t& selected, bool& edited);
 
     void disabled_begin(bool disabled);
     void disabled_end();
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 0881231c9..d16304dbc 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -701,7 +701,6 @@ struct Sidebar::priv
 
     wxBoxSizer *sizer_params;
     FreqChangedParams   *frequently_changed_parameters{ nullptr };
-    SearchComboBox      *search_cb{ nullptr };
     ObjectList          *object_list{ nullptr };
     ObjectManipulation  *object_manipulation{ nullptr };
     ObjectSettings      *object_settings{ nullptr };
@@ -715,6 +714,9 @@ struct Sidebar::priv
     ScalableButton *btn_remove_device;
 	ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
 
+    SearchOptions		search_list;
+    std::string         search_line;
+
     priv(Plater *plater) : plater(plater) {}
     ~priv();
 
@@ -828,10 +830,6 @@ Sidebar::Sidebar(Plater *parent)
     p->frequently_changed_parameters = new FreqChangedParams(p->scrolled);
     p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
 
-    // Search combobox
-//    p->search_cb = new SearchComboBox(p->scrolled);
-//    p->sizer_params->Add(p->search_cb, 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
-
     // Object List
     p->object_list = new ObjectList(p->scrolled);
     p->sizer_params->Add(p->object_list->get_sizer(), 1, wxEXPAND);
@@ -1085,6 +1083,17 @@ void Sidebar::msw_rescale()
     p->scrolled->Layout();
 }
 
+void Sidebar::apply_search_filter()
+{
+    p->search_list.apply_filters(p->search_line);
+}
+
+void Sidebar::jump_to_option(size_t selected)
+{
+    const SearchOptions::Option& opt = p->search_list.get_option(selected);
+    wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key, opt.category);
+}
+
 ObjectManipulation* Sidebar::obj_manipul()
 {
     return p->object_manipulation;
@@ -1357,21 +1366,14 @@ static std::vector<SearchInput> get_search_inputs(ConfigOptionMode mode)
 
 void Sidebar::update_search_list()
 {
-    if (p->search_cb)
-        p->search_cb->init(get_search_inputs(m_mode));
-
-    std::vector<SearchInput> search_list{};
+    p->search_list.init(get_search_inputs(m_mode));
 
     auto& tabs_list = wxGetApp().tabs_list;
     auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
 
     for (auto tab : tabs_list)
         if (tab->supports_printer_technology(print_tech))
-            search_list.emplace_back(SearchInput{ tab->get_config(), tab->type(), m_mode });
-
-    for (auto tab : tabs_list)
-        if (tab->supports_printer_technology(print_tech))
-            tab->get_search_cb()->init(search_list);
+            tab->get_search_cb()->init(p->search_list);
 }
 
 void Sidebar::update_mode()
@@ -1398,6 +1400,16 @@ std::vector<PresetComboBox*>& Sidebar::combos_filament()
     return p->combos_filament;
 }
 
+SearchOptions& Sidebar::get_search_list()
+{
+    return p->search_list;
+}
+
+std::string& Sidebar::get_search_line()
+{
+    return p->search_line;
+}
+
 // Plater::DropTarget
 
 class PlaterDropTarget : public wxFileDropTarget
@@ -2143,6 +2155,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
     view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
     view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
     view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
+    view3D_canvas->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { /*this->redo();*/ });
     view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
     view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
     view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
@@ -5249,6 +5262,18 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou
     out_text = "";
 }
 
+bool Plater::search_string_getter(int idx, const char** out_text)
+{
+    const SearchOptions& search_list = p->sidebar->get_search_list();
+    
+    if (0 <= idx && (size_t)idx < search_list.size()) {
+        search_list[idx].get_label(out_text);
+        return true;
+    }
+
+    return false;
+}
+
 void Plater::on_extruders_change(size_t num_extruders)
 {
     auto& choices = sidebar().combos_filament();
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index a51639cc5..f8426bf21 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -12,6 +12,7 @@
 
 #include "libslic3r/BoundingBox.hpp"
 #include "wxExtensions.hpp"
+#include "SearchComboBox.hpp"
 
 class wxButton;
 class ScalableButton;
@@ -100,6 +101,8 @@ public:
     void update_mode_sizer() const;
     void update_reslice_btn_tooltip() const;
     void msw_rescale();
+    void apply_search_filter();
+    void jump_to_option(size_t selected);
 
     ObjectManipulation*     obj_manipul();
     ObjectList*             obj_list();
@@ -125,7 +128,10 @@ public:
     void                    update_mode();
     void                    update_search_list();
 
-    std::vector<PresetComboBox*>& combos_filament();
+    std::vector<PresetComboBox*>&   combos_filament();
+    SearchOptions&                  get_search_list();
+    std::string&                    get_search_line();
+
 private:
     struct priv;
     std::unique_ptr<priv> p;
@@ -220,6 +226,7 @@ public:
     void redo_to(int selection);
     bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text);
     void undo_redo_topmost_string_getter(const bool is_undo, std::string& out_text);
+    bool search_string_getter(int idx, const char **out_text);
     // For the memory statistics. 
     const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
     // Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo.
diff --git a/src/slic3r/GUI/SearchComboBox.cpp b/src/slic3r/GUI/SearchComboBox.cpp
index a2efd5b02..efe46073b 100644
--- a/src/slic3r/GUI/SearchComboBox.cpp
+++ b/src/slic3r/GUI/SearchComboBox.cpp
@@ -48,6 +48,10 @@ bool SearchOptions::Option::is_matched_option(const wxString& search, int& outSc
             fts::fuzzy_match(search_pattern, opt_key_str , outScore)   ); 
 }
 
+void SearchOptions::Filter::get_label(const char** out_text) const
+{
+    *out_text = label.utf8_str();
+}
 
 template<class T>
 void change_opt_key(std::string& opt_key, DynamicPrintConfig* config)
@@ -82,19 +86,40 @@ void SearchOptions::append_options(DynamicPrintConfig* config, Preset::Type type
             label += _(opt.category) + " : ";
         label += _(opt.full_label.empty() ? opt.label : opt.full_label);
 
-        options.emplace_back(Option{ label, opt_key, opt.category, type });
+        if (!label.IsEmpty())
+            options.emplace_back(Option{ label, opt_key, opt.category, type });
     }
 }
 
-void SearchOptions::apply_filters(const wxString& search)
+void SearchOptions::apply_filters(const std::string& search)
 {
     clear_filters();
-    for (auto option : options) {
-        int score;
-        if (option.is_matched_option(search, score))
-            filters.emplace_back(Filter{ option.label, score });
+
+    bool full_list = search.empty();
+    for (size_t i=0; i < options.size(); i++) {
+        int score=0;
+        if (full_list || options[i].is_matched_option(search, score))
+            filters.emplace_back(Filter{ options[i].label, i, score });
     }
-    sort_filters();
+
+    if (!full_list)
+        sort_filters();
+}
+
+void SearchOptions::init(std::vector<SearchInput> input_values)
+{
+    clear_options();
+    for (auto i : input_values)
+        append_options(i.config, i.type, i.mode);
+    sort_options();
+
+    apply_filters("");
+}
+
+const SearchOptions::Option& SearchOptions::get_option(size_t pos_in_filter) const
+{
+    assert(pos_in_filter != size_t(-1) && filters[pos_in_filter].option_idx != size_t(-1));
+    return options[filters[pos_in_filter].option_idx];
 }
 
 SearchComboBox::SearchComboBox(wxWindow *parent) :
@@ -177,6 +202,13 @@ void SearchComboBox::init(std::vector<SearchInput> input_values)
     update_combobox();
 }
 
+void SearchComboBox::init(const SearchOptions& new_search_list)
+{
+    search_list = new_search_list;
+
+    update_combobox();
+}
+
 void SearchComboBox::update_combobox()
 {
     wxString search_str = this->GetValue();
diff --git a/src/slic3r/GUI/SearchComboBox.hpp b/src/slic3r/GUI/SearchComboBox.hpp
index 482bb18eb..6b93ce9ac 100644
--- a/src/slic3r/GUI/SearchComboBox.hpp
+++ b/src/slic3r/GUI/SearchComboBox.hpp
@@ -42,14 +42,17 @@ public:
 
     struct Filter {
         wxString        label;
+        size_t          option_idx {0};
         int             outScore {0};
+
+        void get_label(const char** out_text) const;
     };
     std::vector<Filter> filters {};
 
     void clear_options() { options.clear(); }
     void clear_filters() { filters.clear(); }
     void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
-    void apply_filters(const wxString& search);
+    void apply_filters(const std::string& search);
 
     void sort_options() {
         std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) {
@@ -59,6 +62,15 @@ public:
         std::sort(filters.begin(), filters.end(), [](const Filter& f1, const Filter& f2) {
             return f1.outScore > f2.outScore; });
     };
+
+    void init(std::vector<SearchInput> input_values);
+    size_t options_size() const { return options.size(); }
+    size_t filters_size() const { return filters.size(); }
+
+    size_t size() const         { return filters_size(); }
+
+    const Filter& operator[](const size_t pos) const noexcept { return filters[pos]; }
+    const Option& get_option(size_t pos_in_filter) const;
 };
 
 class SearchComboBox : public wxBitmapComboBox
@@ -86,6 +98,7 @@ public:
 
     void	init(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
     void    init(std::vector<SearchInput> input_values);
+    void    init(const SearchOptions& new_search_list);
     void    update_combobox();
 
 private:

From ab02d344e4a05a45527598e92404410392cbf8e8 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Sun, 29 Mar 2020 20:52:14 +0200
Subject: [PATCH 07/55] Activated "collapsed sidebar" button

---
 src/slic3r/GUI/GLCanvas3D.cpp | 4 ++++
 src/slic3r/GUI/Plater.cpp     | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index f67fea7a5..5c109ca7f 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4463,7 +4463,11 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
     bool action_taken = false;
     ImGuiWrapper* imgui = wxGetApp().imgui();
 
+#if ENABLE_NON_STATIC_CANVAS_MANAGER
+    const float x = pos_x * (float)wxGetApp().plater()->get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
+#else
     const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
+#endif
     imgui->set_next_window_pos(x, m_undoredo_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
     std::string title = L("Search");
     imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index a04698a47..a31b0aee2 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -2199,7 +2199,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
     view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
     view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
     view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
-    view3D_canvas->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { /*this->redo();*/ });
+    view3D_canvas->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebur(!this->q->is_sidebar_collapsed());  });
     view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
     view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
     view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });

From abad9133ebfd48a1e048203cf1a42379207e4890 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 30 Mar 2020 11:53:58 +0200
Subject: [PATCH 08/55] Added new icon for "collapse sidebar" + The tooltip for
 a "Collapse" button is updated according to the collapse state

---
 resources/icons/collapse.svg  | 16 ++++++++++++++++
 src/slic3r/GUI/GLCanvas3D.cpp | 17 ++++++++++++++---
 src/slic3r/GUI/GLToolbar.cpp  |  6 ++++++
 src/slic3r/GUI/GLToolbar.hpp  |  2 ++
 4 files changed, 38 insertions(+), 3 deletions(-)
 create mode 100644 resources/icons/collapse.svg

diff --git a/resources/icons/collapse.svg b/resources/icons/collapse.svg
new file mode 100644
index 000000000..c0d6f43d5
--- /dev/null
+++ b/resources/icons/collapse.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="cross">
+	<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="12" y1="1" x2="15" y2="4"/></g>
+	<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="12" y1="7" x2="15" y2="4"/></g>
+	<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8"  y1="1" x2="11" y2="4"/></g>
+	<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8"  y1="7" x2="11" y2="4"/></g>
+	
+	<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="9"  x2="1" y2="12"/></g>
+	<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="15" x2="1" y2="12"/></g>
+	<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="9"  x2="5" y2="12"/></g>
+	<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="15" x2="5" y2="12"/></g>
+</g>
+</svg>
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 5c109ca7f..c31995e62 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -5161,10 +5161,21 @@ bool GLCanvas3D::_init_undoredo_toolbar()
         return false;
 
     item.name = "collapse_sidebar";
-    item.icon_filename = "cross.svg";
-    item.tooltip = _utf8(L("Collapse right panel"));
+    item.icon_filename = "collapse.svg";
+    item.tooltip = wxGetApp().plater()->is_sidebar_collapsed() ? 
+                   _utf8(L("Expand right panel")) : _utf8(L("Collapse right panel"));
     item.sprite_id = 2;
-    item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR)); };
+    item.left.action_callback = [this, item]() {
+        std::string new_tooltip = wxGetApp().plater()->is_sidebar_collapsed() ? 
+                                  _utf8(L("Collapse right panel")) : _utf8(L("Expand right panel"));
+
+        int id = m_undoredo_toolbar.get_item_id("collapse_sidebar");
+        m_undoredo_toolbar.set_tooltip(id, new_tooltip);
+        set_tooltip("");
+
+        post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR));
+    };
+
     item.enabling_callback = []()->bool { return true; };
     if (!m_undoredo_toolbar.add_item(item))
         return false;
diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp
index 4219fe482..0bd087814 100644
--- a/src/slic3r/GUI/GLToolbar.cpp
+++ b/src/slic3r/GUI/GLToolbar.cpp
@@ -406,6 +406,12 @@ void GLToolbar::set_additional_tooltip(int item_id, const std::string& text)
         m_items[item_id]->set_additional_tooltip(text);
 }
 
+void GLToolbar::set_tooltip(int item_id, const std::string& text)
+{
+    if (0 <= item_id && item_id < (int)m_items.size())
+        m_items[item_id]->set_tooltip(text);
+}
+
 bool GLToolbar::update_items_state()
 {
     bool ret = false;
diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp
index b4aac9206..27b43fef6 100644
--- a/src/slic3r/GUI/GLToolbar.hpp
+++ b/src/slic3r/GUI/GLToolbar.hpp
@@ -118,6 +118,7 @@ public:
     const std::string& get_tooltip() const { return m_data.tooltip; }
     const std::string& get_additional_tooltip() const { return m_data.additional_tooltip; }
     void set_additional_tooltip(const std::string& text) { m_data.additional_tooltip = text; }
+    void set_tooltip(const std::string& text)            { m_data.tooltip = text; }
 
     void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); }
     void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); }
@@ -317,6 +318,7 @@ public:
 
     void get_additional_tooltip(int item_id, std::string& text);
     void set_additional_tooltip(int item_id, const std::string& text);
+    void set_tooltip(int item_id, const std::string& text);
 
     // returns true if any item changed its state
     bool update_items_state();

From 042880ba2df913916b2cc77f7bb677e07bfd2c58 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 31 Mar 2020 22:46:12 +0200
Subject: [PATCH 09/55] Search: Implemented highlighting of a letters from the
 search string

---
 src/imgui/imconfig.h              | 11 +++-
 src/imgui/imgui_draw.cpp          | 14 +++++
 src/slic3r/GUI/SearchComboBox.cpp | 98 ++++++++++++++++++++++++++++---
 src/slic3r/GUI/SearchComboBox.hpp |  8 ++-
 4 files changed, 118 insertions(+), 13 deletions(-)

diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index 09bfd16c9..26dccd5c3 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -97,9 +97,14 @@
 //#define IMGUI_DEBUG_PARANOID
 
 //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
-/*
+
 namespace ImGui
 {
-    void MyFunction(const char* name, const MyMatrix44& v);
+    // Special ASCII characters STX and ETX are used here as markup symbols for tokens to be highlighted.
+    const char ColorMarkerStart = 0x2; // STX
+    const char ColorMarkerEnd   = 0x3; // ETX
+
+//    void MyFunction(const char* name, const MyMatrix44& v);
+
 }
-*/
+
diff --git a/src/imgui/imgui_draw.cpp b/src/imgui/imgui_draw.cpp
index 4bb91ccfe..bee1fdfa7 100644
--- a/src/imgui/imgui_draw.cpp
+++ b/src/imgui/imgui_draw.cpp
@@ -33,6 +33,7 @@ Index of this file:
 #define IMGUI_DEFINE_MATH_OPERATORS
 #endif
 #include "imgui_internal.h"
+#include "imconfig.h"
 
 #include <stdio.h>      // vsnprintf, sscanf, printf
 #if !defined(alloca)
@@ -2991,6 +2992,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
     ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
     unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
 
+    ImU32 defaultCol = col;
+
     while (s < text_end)
     {
         if (word_wrap_enabled)
@@ -3019,6 +3022,17 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
             }
         }
 
+        if (*s == ImGui::ColorMarkerStart) {
+            col = ImGui::GetColorU32(ImGuiCol_ButtonHovered);
+            s += 1;
+        }
+        else if (*s == ImGui::ColorMarkerEnd) {
+            col = defaultCol;
+            s += 1;
+            if (s == text_end)
+                break;
+        }
+
         // Decode and advance source
         unsigned int c = (unsigned int)*s;
         if (c < 0x80)
diff --git a/src/slic3r/GUI/SearchComboBox.cpp b/src/slic3r/GUI/SearchComboBox.cpp
index efe46073b..1e1ab738f 100644
--- a/src/slic3r/GUI/SearchComboBox.cpp
+++ b/src/slic3r/GUI/SearchComboBox.cpp
@@ -23,14 +23,15 @@
 #define FTS_FUZZY_MATCH_IMPLEMENTATION
 #include "fts_fuzzy_match.h"
 
+#include "imgui/imconfig.h"
+
 using boost::optional;
 
 namespace Slic3r {
 namespace GUI {
 
-bool SearchOptions::Option::containes(const wxString& search_) const
+bool SearchOptions::Option::fuzzy_match_simple(char const * search_pattern) const
 {
-    char const* search_pattern = search_.utf8_str();
     char const* opt_key_str    = opt_key.c_str();
     char const* label_str      = label.utf8_str();
 
@@ -38,9 +39,20 @@ bool SearchOptions::Option::containes(const wxString& search_) const
             fts::fuzzy_match_simple(search_pattern, opt_key_str )   ; 
 }
 
-bool SearchOptions::Option::is_matched_option(const wxString& search, int& outScore)
+bool SearchOptions::Option::fuzzy_match_simple(const wxString& search) const
 {
     char const* search_pattern = search.utf8_str();
+    return fuzzy_match_simple(search_pattern);
+}
+
+bool SearchOptions::Option::fuzzy_match_simple(const std::string& search) const
+{
+    char const* search_pattern = search.c_str();
+    return fuzzy_match_simple(search_pattern);
+}
+
+bool SearchOptions::Option::fuzzy_match(char const* search_pattern, int& outScore)
+{
     char const* opt_key_str    = opt_key.c_str();
     char const* label_str      = label.utf8_str();
 
@@ -48,6 +60,18 @@ bool SearchOptions::Option::is_matched_option(const wxString& search, int& outSc
             fts::fuzzy_match(search_pattern, opt_key_str , outScore)   ); 
 }
 
+bool SearchOptions::Option::fuzzy_match(const wxString& search, int& outScore)
+{
+    char const* search_pattern = search.utf8_str();
+    return fuzzy_match(search_pattern, outScore); 
+}
+
+bool SearchOptions::Option::fuzzy_match(const std::string& search, int& outScore)
+{
+    char const* search_pattern = search.c_str();
+    return fuzzy_match(search_pattern, outScore);
+}
+
 void SearchOptions::Filter::get_label(const char** out_text) const
 {
     *out_text = label.utf8_str();
@@ -91,15 +115,73 @@ void SearchOptions::append_options(DynamicPrintConfig* config, Preset::Type type
     }
 }
 
+// Wrap a string with ColorMarkerStart and ColorMarkerEnd symbols
+static wxString wrap_string(const wxString& str)
+{
+    return wxString::Format("%c%s%c", ImGui::ColorMarkerStart, str, ImGui::ColorMarkerEnd);
+}
+
+// Mark a string using ColorMarkerStart and ColorMarkerEnd symbols
+static void mark_string(wxString& str, const wxString& search_str)
+{
+    // Try to find whole search string
+    if (str.Replace(search_str, wrap_string(search_str), false) != 0)
+        return;
+
+    // Try to find whole capitalized search string
+    wxString search_str_capitalized = search_str.Capitalize();
+    if (str.Replace(search_str_capitalized, wrap_string(search_str_capitalized), false) != 0)
+        return;
+
+    // if search string is just a one letter now, there is no reason to continue 
+    if (search_str.Len()==1)
+        return;
+
+    // Split a search string for two strings (string without last letter and last letter)
+    // and repeat a function with new search strings
+    mark_string(str, search_str.SubString(0, search_str.Len() - 2));
+    mark_string(str, search_str.Last());
+}
+
+// clear marked string from a redundant use of ColorMarkers
+static void clear_marked_string(wxString& str)
+{
+    // Check if the string has a several ColorMarkerStart in a row and replace them to only one, if any
+    wxString delete_string = wxString::Format("%c%c", ImGui::ColorMarkerStart, ImGui::ColorMarkerStart);
+    if (str.Replace(delete_string, ImGui::ColorMarkerStart, true) != 0) {
+        // If there were several ColorMarkerStart in a row, it means there should be a several ColorMarkerStop in a row,
+        // replace them to only one
+        delete_string = wxString::Format("%c%c", ImGui::ColorMarkerEnd, ImGui::ColorMarkerEnd);
+        str.Replace(delete_string, ImGui::ColorMarkerEnd, true);
+    }
+
+    // And we should to remove redundant ColorMarkers, if they are in "End, Start" sequence in a row
+    delete_string = wxString::Format("%c%c", ImGui::ColorMarkerEnd, ImGui::ColorMarkerStart);
+    str.Replace(delete_string, wxEmptyString, true);
+}
+
 void SearchOptions::apply_filters(const std::string& search)
 {
     clear_filters();
 
     bool full_list = search.empty();
-    for (size_t i=0; i < options.size(); i++) {
-        int score=0;
-        if (full_list || options[i].is_matched_option(search, score))
-            filters.emplace_back(Filter{ options[i].label, i, score });
+
+    for (size_t i=0; i < options.size(); i++)
+    {
+        if (full_list) {
+            filters.emplace_back(Filter{ options[i].label, i, 0 });
+            continue;
+        }
+
+        int score = 0;
+        if (options[i].fuzzy_match_simple(search)/*fuzzy_match(search, score)*/)
+        {
+            wxString label = options[i].label;
+            mark_string(label, from_u8(search));
+            clear_marked_string(label);
+
+            filters.emplace_back(Filter{ label, i, score });
+        }
     }
 
     if (!full_list)
@@ -243,7 +325,7 @@ void SearchComboBox::append_items(const wxString& search)
 */
 
     for (const SearchOptions::Option& option : search_list.options)
-        if (option.containes(search))
+        if (option.fuzzy_match_simple(search))
             append(option.label, (void*)&option);
 
     SuppressUpdate su(this);
diff --git a/src/slic3r/GUI/SearchComboBox.hpp b/src/slic3r/GUI/SearchComboBox.hpp
index 6b93ce9ac..294395b72 100644
--- a/src/slic3r/GUI/SearchComboBox.hpp
+++ b/src/slic3r/GUI/SearchComboBox.hpp
@@ -35,8 +35,12 @@ public:
         Preset::Type    type {Preset::TYPE_INVALID};
         // wxString     grope;
 
-        bool containes(const wxString& search) const;
-        bool is_matched_option(const wxString &search, int &outScore);
+        bool fuzzy_match_simple(char const *search_pattern) const;
+        bool fuzzy_match_simple(const wxString& search) const;
+        bool fuzzy_match_simple(const std::string &search) const;
+        bool fuzzy_match(char const *search_pattern, int &outScore);
+        bool fuzzy_match(const wxString &search, int &outScore);
+        bool fuzzy_match(const std::string &search, int &outScore);
     };
     std::vector<Option> options {};
 

From 67c55c74901f1d337ef08f2090a87cfb4263bb0f Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 1 Apr 2020 11:51:44 +0200
Subject: [PATCH 10/55] Added code for deactivating of search toolbar item  +
 invalidated its unused callbacks.

 + ImGui::Selectable function is copied to InGuiWrapper.cpp and a little beat modified to change a label text, when item is hovered
---
 src/imgui/imconfig.h            |   3 +
 src/imgui/imgui_draw.cpp        |   9 +-
 src/slic3r/GUI/GLCanvas3D.cpp   |  32 +++++---
 src/slic3r/GUI/GLCanvas3D.hpp   |   1 +
 src/slic3r/GUI/ImGuiWrapper.cpp | 140 +++++++++++++++++++++++++++++++-
 5 files changed, 173 insertions(+), 12 deletions(-)

diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index 26dccd5c3..c425bbef2 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -100,6 +100,9 @@
 
 namespace ImGui
 {
+    // Special ASCII character is used here as markup symbols for tokens to be highlighted as a for hovered item
+    const char ColorMarkerHovered   = 0x1; // STX
+
     // Special ASCII characters STX and ETX are used here as markup symbols for tokens to be highlighted.
     const char ColorMarkerStart = 0x2; // STX
     const char ColorMarkerEnd   = 0x3; // ETX
diff --git a/src/imgui/imgui_draw.cpp b/src/imgui/imgui_draw.cpp
index bee1fdfa7..4e6f1374b 100644
--- a/src/imgui/imgui_draw.cpp
+++ b/src/imgui/imgui_draw.cpp
@@ -2993,6 +2993,13 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
     unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
 
     ImU32 defaultCol = col;
+    ImU32 highlighCol = ImGui::GetColorU32(ImGuiCol_ButtonHovered);
+
+    // if text is started with ColorMarkerHovered symbol, we should use another color for a highlighting
+    if (*s == ImGui::ColorMarkerHovered) {
+        highlighCol = ImGui::GetColorU32(ImGuiCol_FrameBg);
+        s += 1;
+    }
 
     while (s < text_end)
     {
@@ -3023,7 +3030,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
         }
 
         if (*s == ImGui::ColorMarkerStart) {
-            col = ImGui::GetColorU32(ImGuiCol_ButtonHovered);
+            col = highlighCol;
             s += 1;
         }
         else if (*s == ImGui::ColorMarkerEnd) {
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index c31995e62..e98d5d9ac 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -2986,7 +2986,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
         return;
     }
 
-    if ((keyCode == WXK_ESCAPE) && _deactivate_undo_redo_toolbar_items())
+    if ((keyCode == WXK_ESCAPE) && (_deactivate_undo_redo_toolbar_items() || _deactivate_search_toolbar_item()))
         return;
 
     if (m_gizmos.on_char(evt))
@@ -3636,6 +3636,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
     else if (evt.Leaving())
     {
         _deactivate_undo_redo_toolbar_items();
+        _deactivate_search_toolbar_item();
 
         // to remove hover on objects when the mouse goes out of this canvas
         m_mouse.position = Vec2d(-1.0, -1.0);
@@ -3643,7 +3644,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
     }
     else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown())
     {
-        if (_deactivate_undo_redo_toolbar_items())
+        if (_deactivate_undo_redo_toolbar_items() || _deactivate_search_toolbar_item())
             return;
 
         // If user pressed left or right button we first check whether this happened
@@ -4488,18 +4489,15 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
     search_line = s;
     delete [] s;
 
+    if (edited)
+        wxGetApp().sidebar().apply_search_filter();
+
     if (selected != size_t(-1))
     {
         wxGetApp().sidebar().jump_to_option(selected);
         action_taken = true;
     }
 
-    if (edited)
-    {
-        wxGetApp().sidebar().apply_search_filter();
-        action_taken = true;
-    }
-
     imgui->end();
 
     return action_taken;
@@ -5041,10 +5039,13 @@ bool GLCanvas3D::_init_main_toolbar()
     item.left.render_callback = [this](float left, float right, float, float) {
         if (m_canvas != nullptr)
         {
-            _render_search_list(0.5f * (left + right));
+            if (_render_search_list(0.5f * (left + right)))
+                _deactivate_search_toolbar_item();
         }
     };
-    item.enabling_callback = []()->bool { return true; };
+    item.left.action_callback   = GLToolbarItem::Default_Action_Callback;
+    item.visibility_callback    = GLToolbarItem::Default_Visibility_Callback;
+    item.enabling_callback      = GLToolbarItem::Default_Enabling_Callback;
     if (!m_main_toolbar.add_item(item))
         return false;
 
@@ -7230,6 +7231,17 @@ bool GLCanvas3D::_deactivate_undo_redo_toolbar_items()
     return false;
 }
 
+bool GLCanvas3D::_deactivate_search_toolbar_item()
+{
+    if (m_main_toolbar.is_item_pressed("search"))
+    {
+        m_main_toolbar.force_left_action(m_main_toolbar.get_item_id("search"), *this);
+        return true;
+    }
+
+    return false;
+}
+
 const Print* GLCanvas3D::fff_print() const
 {
     return (m_process == nullptr) ? nullptr : m_process->fff_print();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 228095a06..17270f759 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -834,6 +834,7 @@ private:
     void _update_selection_from_hover();
 
     bool _deactivate_undo_redo_toolbar_items();
+    bool _deactivate_search_toolbar_item();
 
     static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
 
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 00b800042..06a54b11c 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -15,6 +15,9 @@
 
 #include <GL/glew.h>
 
+#ifndef IMGUI_DEFINE_MATH_OPERATORS
+#define IMGUI_DEFINE_MATH_OPERATORS
+#endif
 #include <imgui/imgui_internal.h>
 
 #include "libslic3r/libslic3r.h"
@@ -400,6 +403,141 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (
     return is_hovered;
 }
 
+// It's a copy of IMGui::Selactable function.
+// But a little beat modified to change a label text.
+// If item is hovered we should use another color for highlighted letters.
+// To do that we push a ColorMarkerHovered symbol at the very beginning of the label
+// This symbol will be used to a color selection for the highlighted letters.
+// see imgui_draw.cpp, void ImFont::RenderText()
+static bool selectable(const char* label, bool selected, ImGuiSelectableFlags flags = 0, const ImVec2& size_arg = ImVec2(0, 0))
+{
+    ImGuiWindow* window = ImGui::GetCurrentWindow();
+    if (window->SkipItems)
+        return false;
+
+    ImGuiContext& g = *GImGui;
+    const ImGuiStyle& style = g.Style;
+
+    if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) // FIXME-OPT: Avoid if vertically clipped.
+        ImGui::PushColumnsBackground();
+
+    ImGuiID id = window->GetID(label);
+    ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
+    ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
+    ImVec2 pos = window->DC.CursorPos;
+    pos.y += window->DC.CurrLineTextBaseOffset;
+    ImRect bb_inner(pos, pos + size);
+    ImGui::ItemSize(size, 0.0f);
+
+    // Fill horizontal space.
+    ImVec2 window_padding = window->WindowPadding;
+    float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? ImGui::GetWindowContentRegionMax().x : ImGui::GetContentRegionMax().x;
+    float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - pos.x);
+    ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
+    ImRect bb(pos, pos + size_draw);
+    if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
+        bb.Max.x += window_padding.x;
+
+    // Selectables are tightly packed together so we extend the box to cover spacing between selectable.
+    const float spacing_x = style.ItemSpacing.x;
+    const float spacing_y = style.ItemSpacing.y;
+    const float spacing_L = IM_FLOOR(spacing_x * 0.50f);
+    const float spacing_U = IM_FLOOR(spacing_y * 0.50f);
+    bb.Min.x -= spacing_L;
+    bb.Min.y -= spacing_U;
+    bb.Max.x += (spacing_x - spacing_L);
+    bb.Max.y += (spacing_y - spacing_U);
+
+    bool item_add;
+    if (flags & ImGuiSelectableFlags_Disabled)
+    {
+        ImGuiItemFlags backup_item_flags = window->DC.ItemFlags;
+        window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus;
+        item_add = ImGui::ItemAdd(bb, id);
+        window->DC.ItemFlags = backup_item_flags;
+    }
+    else
+    {
+        item_add = ImGui::ItemAdd(bb, id);
+    }
+    if (!item_add)
+    {
+        if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns)
+            ImGui::PopColumnsBackground();
+        return false;
+    }
+
+    // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
+    ImGuiButtonFlags button_flags = 0;
+    if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
+    if (flags & ImGuiSelectableFlags_PressedOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
+    if (flags & ImGuiSelectableFlags_PressedOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
+    if (flags & ImGuiSelectableFlags_Disabled) { button_flags |= ImGuiButtonFlags_Disabled; }
+    if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
+    if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; }
+
+    if (flags & ImGuiSelectableFlags_Disabled)
+        selected = false;
+
+    const bool was_selected = selected;
+    bool hovered, held;
+    bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, button_flags);
+
+    // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
+    if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
+    {
+        if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
+        {
+            g.NavDisableHighlight = true;
+            ImGui::SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent);
+        }
+    }
+    if (pressed)
+        ImGui::MarkItemEdited(id);
+
+    if (flags & ImGuiSelectableFlags_AllowItemOverlap)
+        ImGui::SetItemAllowOverlap();
+
+    // In this branch, Selectable() cannot toggle the selection so this will never trigger.
+    if (selected != was_selected) //-V547
+        window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
+
+    // Render
+    if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld))
+        hovered = true;
+    if (hovered || selected)
+    {
+        const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
+        ImGui::RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
+        ImGui::RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
+    }
+
+    if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns)
+    {
+        ImGui::PopColumnsBackground();
+        bb.Max.x -= (ImGui::GetContentRegionMax().x - max_x);
+    }
+
+    // mark a label with a ImGui::ColorMarkerHovered, if item is hovered
+    char* marked_label = new char[255];
+    if (hovered)
+        sprintf(marked_label, "%c%s", ImGui::ColorMarkerHovered, label);
+    else
+        strcpy(marked_label, label);
+
+    if (flags & ImGuiSelectableFlags_Disabled) ImGui::PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
+    ImGui::RenderTextClipped(bb_inner.Min, bb_inner.Max, marked_label, NULL, &label_size, style.SelectableTextAlign, &bb);
+    if (flags & ImGuiSelectableFlags_Disabled) ImGui::PopStyleColor();
+
+    delete[] marked_label;
+
+    // Automatically close popups
+    if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) ImGui::CloseCurrentPopup();
+
+    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
+    return pressed;
+}
+
 void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char**), char* search_str, size_t& selected, bool& edited)
 {
     // ImGui::ListBoxHeader("", size);
@@ -442,7 +580,7 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
     const char* item_text;
     while (items_getter(i, &item_text))
     {
-        ImGui::Selectable(item_text, false);
+        selectable(item_text, false);
 
         if (ImGui::IsItemHovered())
             ImGui::SetTooltip("%s", item_text);

From 5ca6b9f8d06843fd0deb6d9ab813c386dfb25183 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 3 Apr 2020 10:01:23 +0200
Subject: [PATCH 11/55] Implemented SearchCtrl class instead of SearchComboBox
 Search string are synchronized between Plater and Tabs.

List with options and filtered list are in Sidebar.
All options list on tabs and Plater use this data from Sidebar

Note: SearchComboBox.cpp(hpp) was renamed to Search.cpp(hpp)
---
 resources/icons/search.svg                    |   5 +-
 src/slic3r/CMakeLists.txt                     |   4 +-
 src/slic3r/GUI/GLCanvas3D.cpp                 |   2 +-
 src/slic3r/GUI/Plater.cpp                     |  17 +-
 src/slic3r/GUI/Plater.hpp                     |   3 +-
 .../GUI/{SearchComboBox.cpp => Search.cpp}    | 204 ++++++++++++++++--
 .../GUI/{SearchComboBox.hpp => Search.hpp}    |  70 +++++-
 src/slic3r/GUI/Tab.cpp                        |  17 +-
 src/slic3r/GUI/Tab.hpp                        |   9 +-
 9 files changed, 283 insertions(+), 48 deletions(-)
 rename src/slic3r/GUI/{SearchComboBox.cpp => Search.cpp} (65%)
 rename src/slic3r/GUI/{SearchComboBox.hpp => Search.hpp} (67%)

diff --git a/resources/icons/search.svg b/resources/icons/search.svg
index bf97904e8..6421c7e05 100644
--- a/resources/icons/search.svg
+++ b/resources/icons/search.svg
@@ -1 +1,4 @@
-<svg fill="#808080" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px"><path d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px">
+<path fill="#808080" d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
+<path fill="#ED6B21" d="M 13.261719 14.867188 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
+</svg>
\ No newline at end of file
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 7aaa1e622..bf30eb15e 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -151,8 +151,8 @@ set(SLIC3R_GUI_SOURCES
     GUI/DoubleSlider.hpp
     GUI/ObjectDataViewModel.cpp
     GUI/ObjectDataViewModel.hpp
-    GUI/SearchComboBox.cpp
-    GUI/SearchComboBox.hpp
+    GUI/Search.cpp
+    GUI/Search.hpp
     Utils/Http.cpp
     Utils/Http.hpp
     Utils/FixModelByWin10.cpp
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 470aafe23..0f9d49281 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4477,7 +4477,7 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
 
     std::string& search_line = wxGetApp().sidebar().get_search_line();
     char *s = new char[255];
-    strcpy(s, search_line.empty() ? _utf8(L("Type here to search")).c_str() : search_line.c_str());
+    strcpy(s, search_line.empty() ? _u8L("Type here to search").c_str() : search_line.c_str());
 
     imgui->search_list(ImVec2(22 * em, 30 * em), &search_string_getter, s, selected, edited);
 
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index d270b6e0d..acf762bfb 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -80,7 +80,6 @@
 #include "../Utils/FixModelByWin10.hpp"
 #include "../Utils/UndoRedo.hpp"
 #include "RemovableDriveManager.hpp"
-#include "SearchComboBox.hpp"
 
 #if ENABLE_NON_STATIC_CANVAS_MANAGER
 #ifdef __APPLE__
@@ -1095,6 +1094,7 @@ void Sidebar::msw_rescale()
 void Sidebar::apply_search_filter()
 {
     p->search_list.apply_filters(p->search_line);
+    apply_search_line_on_tabs();
 }
 
 void Sidebar::jump_to_option(size_t selected)
@@ -1373,16 +1373,21 @@ static std::vector<SearchInput> get_search_inputs(ConfigOptionMode mode)
     return ret;
 }
 
-void Sidebar::update_search_list()
+void Sidebar::apply_search_line_on_tabs()
 {
-    p->search_list.init(get_search_inputs(m_mode));
-
     auto& tabs_list = wxGetApp().tabs_list;
     auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
 
     for (auto tab : tabs_list)
         if (tab->supports_printer_technology(print_tech))
-            tab->get_search_cb()->init(p->search_list);
+            //tab->get_search_cb()->update_combobox();
+            tab->set_search_line(p->search_line);
+}
+
+void Sidebar::update_search_list()
+{
+    p->search_list.init(get_search_inputs(m_mode));
+    apply_search_line_on_tabs();
 }
 
 void Sidebar::update_mode()
@@ -5345,7 +5350,7 @@ bool Plater::search_string_getter(int idx, const char** out_text)
     const SearchOptions& search_list = p->sidebar->get_search_list();
     
     if (0 <= idx && (size_t)idx < search_list.size()) {
-        search_list[idx].get_label(out_text);
+        search_list[idx].get_marked_label(out_text);
         return true;
     }
 
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 1fb0eab30..6544d4554 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -12,7 +12,7 @@
 
 #include "libslic3r/BoundingBox.hpp"
 #include "wxExtensions.hpp"
-#include "SearchComboBox.hpp"
+#include "Search.hpp"
 
 class wxButton;
 class ScalableButton;
@@ -132,6 +132,7 @@ public:
     void                    update_mode();
     bool                    is_collapsed();
     void                    collapse(bool collapse);
+    void                    apply_search_line_on_tabs();
     void                    update_search_list();
 
     std::vector<PresetComboBox*>&   combos_filament();
diff --git a/src/slic3r/GUI/SearchComboBox.cpp b/src/slic3r/GUI/Search.cpp
similarity index 65%
rename from src/slic3r/GUI/SearchComboBox.cpp
rename to src/slic3r/GUI/Search.cpp
index 1e1ab738f..f3a0386b5 100644
--- a/src/slic3r/GUI/SearchComboBox.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -1,4 +1,4 @@
-#include "SearchComboBox.hpp"
+#include "Search.hpp"
 
 #include <cstddef>
 #include <algorithm>
@@ -13,8 +13,7 @@
 #include <boost/filesystem/operations.hpp>
 #include <boost/log/trivial.hpp>
 
-#include <wx/sizer.h>
-#include <wx/bmpcbox.h>
+//#include <wx/bmpcbox.h>
 #include "libslic3r/PrintConfig.hpp"
 #include "GUI_App.hpp"
 #include "Tab.hpp"
@@ -77,6 +76,11 @@ void SearchOptions::Filter::get_label(const char** out_text) const
     *out_text = label.utf8_str();
 }
 
+void SearchOptions::Filter::get_marked_label(const char** out_text) const
+{
+    *out_text = marked_label.utf8_str();
+}
+
 template<class T>
 void change_opt_key(std::string& opt_key, DynamicPrintConfig* config)
 {
@@ -169,18 +173,18 @@ void SearchOptions::apply_filters(const std::string& search)
     for (size_t i=0; i < options.size(); i++)
     {
         if (full_list) {
-            filters.emplace_back(Filter{ options[i].label, i, 0 });
+            filters.emplace_back(Filter{ options[i].label, options[i].label, i, 0 });
             continue;
         }
 
         int score = 0;
         if (options[i].fuzzy_match_simple(search)/*fuzzy_match(search, score)*/)
         {
-            wxString label = options[i].label;
-            mark_string(label, from_u8(search));
-            clear_marked_string(label);
+            wxString marked_label = options[i].label;
+            mark_string(marked_label, from_u8(search));
+            clear_marked_string(marked_label);
 
-            filters.emplace_back(Filter{ label, i, score });
+            filters.emplace_back(Filter{ options[i].label, marked_label, i, score });
         }
     }
 
@@ -203,23 +207,24 @@ const SearchOptions::Option& SearchOptions::get_option(size_t pos_in_filter) con
     assert(pos_in_filter != size_t(-1) && filters[pos_in_filter].option_idx != size_t(-1));
     return options[filters[pos_in_filter].option_idx];
 }
-
-SearchComboBox::SearchComboBox(wxWindow *parent) :
+/*
+SearchComboBox::SearchComboBox(wxWindow *parent, SearchOptions& search_list) :
 wxBitmapComboBox(parent, wxID_ANY, _(L("Type here to search")) + dots, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1)),
-    em_unit(wxGetApp().em_unit())
+    em_unit(wxGetApp().em_unit()),
+    search_list(search_list)
 {
     SetFont(wxGetApp().normal_font());
     default_search_line = search_line = _(L("Type here to search")) + dots;
     bmp = ScalableBitmap(this, "search");
 
-#ifdef _WIN32
-    // Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that
-    // the index of the item inside CBN_EDITCHANGE may no more be valid.
-//    EnableTextChangedEvents(false);
-#endif /* _WIN32 */
-
     Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
         auto selected_item = this->GetSelection();
+        if (selected_item < 0)
+            return;
+
+        wxGetApp().sidebar().jump_to_option(selected_item);
+
+        return;
         SearchOptions::Option* opt = reinterpret_cast<SearchOptions::Option*>(this->GetClientData(selected_item));
         wxGetApp().get_tab(opt->type)->activate_option(opt->opt_key, opt->category);
 
@@ -230,7 +235,7 @@ wxBitmapComboBox(parent, wxID_ANY, _(L("Type here to search")) + dots, wxDefault
     });
 
     Bind(wxEVT_TEXT, [this](wxCommandEvent &e) {
-        if (prevent_update)
+/*        if (prevent_update)
             return;
 
         if (this->IsTextEmpty())
@@ -238,7 +243,10 @@ wxBitmapComboBox(parent, wxID_ANY, _(L("Type here to search")) + dots, wxDefault
             return;
         }
 
-        if (search_line != this->GetValue()) {
+ * /       if (search_line != this->GetValue()) {
+            std::string& search_str = wxGetApp().sidebar().get_search_line();
+            search_str = into_u8(this->GetValue());
+            wxGetApp().sidebar().apply_search_filter();
             update_combobox();
             search_line = this->GetValue();
         }
@@ -293,6 +301,14 @@ void SearchComboBox::init(const SearchOptions& new_search_list)
 
 void SearchComboBox::update_combobox()
 {
+    this->Clear();
+    for (const SearchOptions::Filter& item : search_list.filters)
+        append(item.label);
+
+//    SuppressUpdate su(this);
+//    this->SetValue(default_search_line);
+
+    return;
     wxString search_str = this->GetValue();
     if (search_str.IsEmpty() || search_str == default_search_line)
         // add whole options list to the controll
@@ -322,7 +338,7 @@ void SearchComboBox::append_items(const wxString& search)
         if (it != search_list.options.end())
             append(it->label, (void*)(&(*it)));
     }
-*/
+* /
 
     for (const SearchOptions::Option& option : search_list.options)
         if (option.fuzzy_match_simple(search))
@@ -332,5 +348,153 @@ void SearchComboBox::append_items(const wxString& search)
     this->SetValue(search);
     this->SetInsertionPointEnd();
 }
+*/
+
+//------------------------------------------
+//          PopupSearchList
+//------------------------------------------
+
+PopupSearchList::PopupSearchList(wxWindow* parent) :
+    wxPopupTransientWindow(parent)
+{
+    panel = new wxPanel(this, wxID_ANY);
+
+    int em_unit = wxGetApp().em_unit();
+
+    search_ctrl = new wxListCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(25 * em_unit, 35 * em_unit), wxLC_NO_HEADER | wxLC_REPORT);
+    search_ctrl->AppendColumn("");
+    search_ctrl->SetColumnWidth(0, 23 * em_unit);
+    search_ctrl->Bind(wxEVT_LIST_ITEM_SELECTED, &PopupSearchList::OnSelect, this);
+
+    wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
+
+    topSizer->Add(search_ctrl, 0, wxEXPAND | wxALL, 2);
+
+    panel->SetSizer(topSizer);
+    
+    topSizer->Fit(panel);
+    SetClientSize(panel->GetSize());
+}
+
+void PopupSearchList::Popup(wxWindow* WXUNUSED(focus))
+{
+    wxPopupTransientWindow::Popup();
+}
+
+void PopupSearchList::OnDismiss()
+{
+    wxPopupTransientWindow::OnDismiss();
+}
+
+bool PopupSearchList::ProcessLeftDown(wxMouseEvent& event)
+{
+    return wxPopupTransientWindow::ProcessLeftDown(event);
+}
+bool PopupSearchList::Show(bool show)
+{
+    return wxPopupTransientWindow::Show(show);
+}
+
+void PopupSearchList::OnSize(wxSizeEvent& event)
+{
+    event.Skip();
+}
+
+void PopupSearchList::OnSetFocus(wxFocusEvent& event)
+{
+    event.Skip();
+}
+
+void PopupSearchList::OnKillFocus(wxFocusEvent& event)
+{
+    event.Skip();
+}
+
+void PopupSearchList::OnSelect(wxListEvent& event)
+{
+    int selection = event.GetIndex();
+    if (selection>=0)
+        wxGetApp().sidebar().jump_to_option(selection);
+
+    OnDismiss();
+}
+
+void PopupSearchList::update_list(std::vector<SearchOptions::Filter>& filters)
+{
+    search_ctrl->DeleteAllItems();
+    for (const SearchOptions::Filter& item : filters)
+        search_ctrl->InsertItem(search_ctrl->GetItemCount(), item.label);
+}
+
+
+//------------------------------------------
+//          SearchCtrl
+//------------------------------------------
+
+SearchCtrl::SearchCtrl(wxWindow* parent):
+    parent(parent)
+{
+    popup_win = new PopupSearchList(parent);
+    box_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+    search_line = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER);
+    search_line->Bind(wxEVT_TEXT, &SearchCtrl::OnInputText, this);
+    search_line->Bind(wxEVT_TEXT_ENTER, &SearchCtrl::PopupList, this);
+    
+    search_btn  = new ScalableButton(parent, wxID_ANY, "search");
+    search_btn->Bind(wxEVT_BUTTON, &SearchCtrl::PopupList, this);
+
+    box_sizer->Add(search_line, 0, wxALIGN_CENTER_VERTICAL);
+    box_sizer->AddSpacer(5);
+    box_sizer->Add(search_btn, 0, wxALIGN_CENTER_VERTICAL);
+}
+
+SearchCtrl::~SearchCtrl()
+{
+    if (search_btn)
+        search_btn->Destroy();
+    if (popup_win)
+        popup_win->Destroy();
+}
+
+void SearchCtrl::OnInputText(wxCommandEvent& )
+{
+    if (prevent_update)
+        return;
+    std::string& search_str = wxGetApp().sidebar().get_search_line();
+    search_str = into_u8(search_line->GetValue());
+    wxGetApp().sidebar().apply_search_filter();
+
+    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
+}
+
+void SearchCtrl::PopupList(wxCommandEvent& )
+{
+    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
+
+    wxPoint pos = search_line->ClientToScreen(wxPoint(0, 0));
+    wxSize sz = search_line->GetSize();
+    pos.x -= sz.GetWidth();
+    popup_win->Position(pos, sz);
+
+    popup_win->Popup();
+}
+
+void SearchCtrl::set_search_line(const std::string& line)
+{
+    prevent_update = true;
+    search_line->SetValue(line.empty() ? _L("Type here to search") : from_u8(line));
+    prevent_update = false;
+}
+
+void SearchCtrl::msw_rescale()
+{
+    wxSize size = wxSize(25 * wxGetApp().em_unit(), -1);
+    // Set rescaled min height to correct layout
+    search_line->SetMinSize(size);
+    // Set rescaled size
+    search_btn->msw_rescale();
+}
+
 
 }}    // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/SearchComboBox.hpp b/src/slic3r/GUI/Search.hpp
similarity index 67%
rename from src/slic3r/GUI/SearchComboBox.hpp
rename to src/slic3r/GUI/Search.hpp
index 294395b72..6718f3af1 100644
--- a/src/slic3r/GUI/SearchComboBox.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -1,11 +1,13 @@
 #ifndef slic3r_SearchComboBox_hpp_
 #define slic3r_SearchComboBox_hpp_
 
-#include <memory>
 #include <vector>
-#include <boost/filesystem/path.hpp>
 
-#include <wx/bmpcbox.h>
+#include <wx/panel.h>
+#include <wx/sizer.h>
+//#include <wx/bmpcbox.h>
+#include <wx/popupwin.h>
+#include <wx/listctrl.h>
 
 #include "Preset.hpp"
 #include "wxExtensions.hpp"
@@ -46,15 +48,20 @@ public:
 
     struct Filter {
         wxString        label;
+        wxString        marked_label;
         size_t          option_idx {0};
         int             outScore {0};
 
         void get_label(const char** out_text) const;
+        void get_marked_label(const char** out_text) const;
     };
     std::vector<Filter> filters {};
 
     void clear_options() { options.clear(); }
     void clear_filters() { filters.clear(); }
+
+    void init(std::vector<SearchInput> input_values);
+
     void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
     void apply_filters(const std::string& search);
 
@@ -67,16 +74,14 @@ public:
             return f1.outScore > f2.outScore; });
     };
 
-    void init(std::vector<SearchInput> input_values);
     size_t options_size() const { return options.size(); }
     size_t filters_size() const { return filters.size(); }
-
     size_t size() const         { return filters_size(); }
 
     const Filter& operator[](const size_t pos) const noexcept { return filters[pos]; }
     const Option& get_option(size_t pos_in_filter) const;
 };
-
+/*
 class SearchComboBox : public wxBitmapComboBox
 {
     class SuppressUpdate
@@ -89,9 +94,10 @@ class SearchComboBox : public wxBitmapComboBox
     };                                                 
 
 public:
-    SearchComboBox(wxWindow *parent);
+    SearchComboBox(wxWindow *parent, SearchOptions& search_list);
     ~SearchComboBox();
 
+    int     append(const wxString& item)                            { return Append(item, bmp.bmp()); }
     int     append(const wxString& item, void* clientData)          { return Append(item, bmp.bmp(), clientData); }
     int     append(const wxString& item, wxClientData* clientData)  { return Append(item, bmp.bmp(), clientData); }
     
@@ -105,8 +111,9 @@ public:
     void    init(const SearchOptions& new_search_list);
     void    update_combobox();
 
+
 private:
-    SearchOptions		search_list;
+    SearchOptions&		search_list;
     wxString            default_search_line;
     wxString            search_line;
 
@@ -115,6 +122,53 @@ private:
 
     ScalableBitmap      bmp;
 };
+*/
+class PopupSearchList : public wxPopupTransientWindow
+{
+public:
+    PopupSearchList(wxWindow* parent);
+    ~PopupSearchList() {}
+
+    // wxPopupTransientWindow virtual methods are all overridden to log them
+    void Popup(wxWindow* focus = NULL) wxOVERRIDE;
+    void OnDismiss() wxOVERRIDE;
+    bool ProcessLeftDown(wxMouseEvent& event) wxOVERRIDE;
+    bool Show(bool show = true) wxOVERRIDE;
+
+    void update_list(std::vector<SearchOptions::Filter>& filters);
+
+private:
+    wxWindow*   panel;
+    wxListCtrl* search_ctrl{ nullptr };
+
+    void OnSize(wxSizeEvent& event);
+    void OnSetFocus(wxFocusEvent& event);
+    void OnKillFocus(wxFocusEvent& event);
+    void OnSelect(wxListEvent& event);
+};
+
+class SearchCtrl
+{
+    wxWindow*           parent      {nullptr};
+    wxBoxSizer*         box_sizer   {nullptr};
+    wxTextCtrl*         search_line {nullptr};
+    ScalableButton*     search_btn  {nullptr};
+    PopupSearchList*    popup_win   {nullptr};
+
+    bool                prevent_update{ false };
+
+    void PopupList(wxCommandEvent& event);
+    void OnInputText(wxCommandEvent& event);
+
+public:
+    SearchCtrl(wxWindow* parent);
+    ~SearchCtrl();
+
+    wxBoxSizer* sizer() const { return box_sizer; }
+
+    void		set_search_line(const std::string& search_line);
+    void        msw_rescale();
+};
 
 }}
 
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 16ee6a789..17bb68d02 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -116,7 +116,8 @@ void Tab::create_preset_tab()
     m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1));
 
     // search combox
-    m_search_cb = new SearchComboBox(panel);
+//    m_search_cb = new SearchComboBox(panel, wxGetApp().sidebar().get_search_list());
+    m_search = new SearchCtrl(panel);
 
     auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
@@ -198,7 +199,8 @@ void Tab::create_preset_tab()
     m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
-    m_hsizer->Add(m_search_cb, 0, wxALIGN_CENTER_VERTICAL);
+//    m_hsizer->Add(m_search_cb, 0, wxALIGN_CENTER_VERTICAL);
+    m_hsizer->Add(m_search->sizer(), 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->AddSpacer(int(16 * scale_factor));
 //    m_hsizer->AddSpacer(int(32 * scale_factor));
 //    m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
@@ -767,9 +769,6 @@ void Tab::update_mode()
     update_visibility();
 
     update_changed_tree_ui();
-
-    // update list of options for search
-//    m_search_cb->init(m_config, type(), m_mode);
 }
 
 void Tab::update_visibility()
@@ -796,7 +795,8 @@ void Tab::msw_rescale()
     m_em_unit = wxGetApp().em_unit();
 
     m_mode_sizer->msw_rescale();
-    m_search_cb->msw_rescale();
+//    m_search_cb->msw_rescale();
+    m_search->msw_rescale();
 
     m_presets_choice->SetSize(35 * m_em_unit, -1);
     m_treectrl->SetMinSize(wxSize(20 * m_em_unit, -1));
@@ -899,6 +899,11 @@ static wxString pad_combo_value_for_config(const DynamicPrintConfig &config)
     return config.opt_bool("pad_enable") ? (config.opt_bool("pad_around_object") ? _("Around object") : _("Below object")) : _("None");
 }
 
+void Tab::set_search_line(const std::string& search_line)
+{
+    m_search->set_search_line(search_line);
+}
+
 void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
 {
     if (wxGetApp().plater() == nullptr) {
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 705a806a3..fe683b49f 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -33,7 +33,7 @@
 #include "Event.hpp"
 #include "wxExtensions.hpp"
 #include "ConfigManipulation.hpp"
-#include "SearchComboBox.hpp"
+#include "Search.hpp"
 
 namespace Slic3r {
 namespace GUI {
@@ -122,7 +122,8 @@ protected:
 	std::string			m_name;
 	const wxString		m_title;
 	PresetBitmapComboBox*	m_presets_choice;
-	SearchComboBox*		m_search_cb;
+//	SearchComboBox*		m_search_cb;
+	SearchCtrl*			m_search;
 	ScalableButton*		m_btn_save_preset;
 	ScalableButton*		m_btn_delete_preset;
 	ScalableButton*		m_btn_hide_incompatible_presets;
@@ -308,9 +309,11 @@ public:
 
 	DynamicPrintConfig*	get_config() { return m_config; }
 	PresetCollection*	get_presets() { return m_presets; }
-	SearchComboBox*     get_search_cb() { return m_search_cb; }
+//	SearchComboBox*     get_search_cb() { return m_search_cb; }
 	size_t				get_selected_preset_item() { return m_selected_preset_item; }
 
+	void			set_search_line(const std::string& search_line);
+
 	void			on_value_change(const std::string& opt_key, const boost::any& value);
 
     void            update_wiping_button_visibility();

From c8cf11b11bc26e5af357891e2ea19468d92043fe Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 3 Apr 2020 12:51:20 +0200
Subject: [PATCH 12/55] Added missed destroy for search_line

---
 src/slic3r/GUI/Search.cpp | 5 +++--
 src/slic3r/GUI/Search.hpp | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index f3a0386b5..9c3a79f3c 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -431,8 +431,7 @@ void PopupSearchList::update_list(std::vector<SearchOptions::Filter>& filters)
 //          SearchCtrl
 //------------------------------------------
 
-SearchCtrl::SearchCtrl(wxWindow* parent):
-    parent(parent)
+SearchCtrl::SearchCtrl(wxWindow* parent)
 {
     popup_win = new PopupSearchList(parent);
     box_sizer = new wxBoxSizer(wxHORIZONTAL);
@@ -451,6 +450,8 @@ SearchCtrl::SearchCtrl(wxWindow* parent):
 
 SearchCtrl::~SearchCtrl()
 {
+    if (search_line)
+        search_line->Destroy();
     if (search_btn)
         search_btn->Destroy();
     if (popup_win)
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 6718f3af1..523457976 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -149,7 +149,7 @@ private:
 
 class SearchCtrl
 {
-    wxWindow*           parent      {nullptr};
+//    wxWindow*           parent      {nullptr};
     wxBoxSizer*         box_sizer   {nullptr};
     wxTextCtrl*         search_line {nullptr};
     ScalableButton*     search_btn  {nullptr};

From 6faae0aa125f8c6bd60fc50b444ad22583a79c52 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 3 Apr 2020 14:29:57 +0200
Subject: [PATCH 13/55] + Fixed clear_marked_string()

---
 src/slic3r/GUI/Search.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 9c3a79f3c..e2e6f864e 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -152,12 +152,11 @@ static void clear_marked_string(wxString& str)
 {
     // Check if the string has a several ColorMarkerStart in a row and replace them to only one, if any
     wxString delete_string = wxString::Format("%c%c", ImGui::ColorMarkerStart, ImGui::ColorMarkerStart);
-    if (str.Replace(delete_string, ImGui::ColorMarkerStart, true) != 0) {
-        // If there were several ColorMarkerStart in a row, it means there should be a several ColorMarkerStop in a row,
-        // replace them to only one
-        delete_string = wxString::Format("%c%c", ImGui::ColorMarkerEnd, ImGui::ColorMarkerEnd);
-        str.Replace(delete_string, ImGui::ColorMarkerEnd, true);
-    }
+    str.Replace(delete_string, ImGui::ColorMarkerStart, true);
+    // If there were several ColorMarkerStart in a row, it means there should be a several ColorMarkerStop in a row,
+    // replace them to only one
+    delete_string = wxString::Format("%c%c", ImGui::ColorMarkerEnd, ImGui::ColorMarkerEnd);
+    str.Replace(delete_string, ImGui::ColorMarkerEnd, true);
 
     // And we should to remove redundant ColorMarkers, if they are in "End, Start" sequence in a row
     delete_string = wxString::Format("%c%c", ImGui::ColorMarkerEnd, ImGui::ColorMarkerStart);

From 17bd52342b9f23fa749b4892706d418b80737dbf Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Sat, 4 Apr 2020 19:25:14 +0200
Subject: [PATCH 14/55] Next experiments with search on Tabs

---
 resources/icons/search_gray.svg |  4 ++
 src/slic3r/GUI/OptionsGroup.hpp | 18 +++++++
 src/slic3r/GUI/Search.cpp       | 94 ++++++++++++++++++++++++++++++---
 src/slic3r/GUI/Search.hpp       | 64 +++++++++++++++++++++-
 4 files changed, 171 insertions(+), 9 deletions(-)
 create mode 100644 resources/icons/search_gray.svg

diff --git a/resources/icons/search_gray.svg b/resources/icons/search_gray.svg
new file mode 100644
index 000000000..043c2e3b2
--- /dev/null
+++ b/resources/icons/search_gray.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px">
+<path fill="#808080" d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
+<path fill="#808080" d="M 13.261719 14.867188 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
+</svg>
\ No newline at end of file
diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp
index 536eb72a5..5b4879f67 100644
--- a/src/slic3r/GUI/OptionsGroup.hpp
+++ b/src/slic3r/GUI/OptionsGroup.hpp
@@ -194,6 +194,24 @@ public:
 #else
         sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5);
 #endif /* __WXGTK__ */
+
+
+
+        if (stb)
+            stb->Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {
+                evt.Skip();
+                const wxSize sz = stb->GetSize();
+                wxPaintDC dc(stb);
+                const wxPen pen = wxPen(wxColour(250, 10, 10), 2, wxPENSTYLE_SOLID);
+                dc.SetPen(pen);
+                dc.SetBrush(wxBrush(wxColour(250, 0, 0), wxBRUSHSTYLE_SOLID));
+                dc.DrawRectangle(5, 5, sz.x - 5, sz.y - 5);
+
+                HDC hdc = GetHdcOf(dc);
+                RECT dim = { 5, 5, sz.x - 5, sz.y - 5 };
+                ::FillRect(hdc, &dim, GetHbrushOf(wxBrush(wxColour( 0, 250,0), wxBRUSHSTYLE_SOLID)));
+
+            });
     }
 
     wxGridSizer*        get_grid_sizer() { return m_grid_sizer; }
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index e2e6f864e..6cdfc1905 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -354,7 +354,7 @@ void SearchComboBox::append_items(const wxString& search)
 //------------------------------------------
 
 PopupSearchList::PopupSearchList(wxWindow* parent) :
-    wxPopupTransientWindow(parent)
+    wxPopupTransientWindow(parent, wxSTAY_ON_TOP| wxBORDER_NONE)
 {
     panel = new wxPanel(this, wxID_ANY);
 
@@ -367,6 +367,19 @@ PopupSearchList::PopupSearchList(wxWindow* parent) :
 
     wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
 
+    wxTextCtrl *text = new wxTextCtrl(panel, wxID_ANY, "Brrrr");
+    text->Bind(wxEVT_ACTIVATE, [](wxEvent& e) {
+        int i=0; });
+    text->Bind(wxEVT_MOUSE_CAPTURE_CHANGED, [](wxEvent& e) {
+        int i = 0; });
+    text->Bind(wxEVT_LEFT_DOWN, [text](wxEvent& e) {
+        text->SetValue("mrrrrrty"); });
+    text->Bind(wxEVT_TEXT, [text](wxCommandEvent& e) {
+        text->SetSelection(2, 3); });
+    text->Bind(wxEVT_CHAR, [text](wxKeyEvent& e) {
+        text->SetFocus(); });
+
+    topSizer->Add(text, 0, wxEXPAND | wxALL, 2);
     topSizer->Add(search_ctrl, 0, wxEXPAND | wxALL, 2);
 
     panel->SetSizer(topSizer);
@@ -432,9 +445,9 @@ void PopupSearchList::update_list(std::vector<SearchOptions::Filter>& filters)
 
 SearchCtrl::SearchCtrl(wxWindow* parent)
 {
-    popup_win = new PopupSearchList(parent);
+//    popup_win = new PopupSearchList(parent);
     box_sizer = new wxBoxSizer(wxHORIZONTAL);
-
+ /*
     search_line = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER);
     search_line->Bind(wxEVT_TEXT, &SearchCtrl::OnInputText, this);
     search_line->Bind(wxEVT_TEXT_ENTER, &SearchCtrl::PopupList, this);
@@ -445,6 +458,28 @@ SearchCtrl::SearchCtrl(wxWindow* parent)
     box_sizer->Add(search_line, 0, wxALIGN_CENTER_VERTICAL);
     box_sizer->AddSpacer(5);
     box_sizer->Add(search_btn, 0, wxALIGN_CENTER_VERTICAL);
+
+*/
+    default_string = _L("Type here to search");
+
+    comboCtrl = new wxComboCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER);
+    comboCtrl->UseAltPopupWindow();
+    popupCtrl = new SearchComboPopup();
+
+    // It is important to call SetPopupControl() as soon as possible
+    comboCtrl->SetPopupControl(popupCtrl);
+    wxBitmap bmp_norm = create_scaled_bitmap("search_gray");
+    wxBitmap bmp_hov = create_scaled_bitmap("search");
+    comboCtrl->SetButtonBitmaps(bmp_norm, true, bmp_hov, bmp_hov, bmp_norm);
+    box_sizer->Add(comboCtrl, 0, wxALIGN_CENTER_VERTICAL);
+
+    popupCtrl->Bind(wxEVT_LISTBOX, &SearchCtrl::OnSelect, this);
+
+    comboCtrl->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
+    comboCtrl->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
+    comboCtrl->Bind(wxEVT_COMBOBOX_DROPDOWN,    &SearchCtrl::PopupList, this);
+
+    comboCtrl->GetTextCtrl()->Bind(wxEVT_LEFT_DOWN,    &SearchCtrl::OnLeftDown, this);
 }
 
 SearchCtrl::~SearchCtrl()
@@ -461,16 +496,20 @@ void SearchCtrl::OnInputText(wxCommandEvent& )
 {
     if (prevent_update)
         return;
+
     std::string& search_str = wxGetApp().sidebar().get_search_line();
-    search_str = into_u8(search_line->GetValue());
+//    search_str = into_u8(search_line->GetValue());
+    search_str = into_u8(comboCtrl->GetValue());
     wxGetApp().sidebar().apply_search_filter();
 
-    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
+//    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
+
+//    update_list(wxGetApp().sidebar().get_search_list().filters);
 }
 
-void SearchCtrl::PopupList(wxCommandEvent& )
+void SearchCtrl::PopupList(wxCommandEvent& e)
 {
-    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
+/*    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
 
     wxPoint pos = search_line->ClientToScreen(wxPoint(0, 0));
     wxSize sz = search_line->GetSize();
@@ -478,13 +517,18 @@ void SearchCtrl::PopupList(wxCommandEvent& )
     popup_win->Position(pos, sz);
 
     popup_win->Popup();
+    */
+    update_list(wxGetApp().sidebar().get_search_list().filters);
+    e.Skip();
 }
 
 void SearchCtrl::set_search_line(const std::string& line)
 {
     prevent_update = true;
-    search_line->SetValue(line.empty() ? _L("Type here to search") : from_u8(line));
+//    search_line->SetValue(line.empty() ? _L("Type here to search") : from_u8(line));
+    comboCtrl->SetText(line.empty() ? default_string : from_u8(line));
     prevent_update = false;
+
 }
 
 void SearchCtrl::msw_rescale()
@@ -494,7 +538,41 @@ void SearchCtrl::msw_rescale()
     search_line->SetMinSize(size);
     // Set rescaled size
     search_btn->msw_rescale();
+
+
+    comboCtrl->SetButtonBitmaps(create_scaled_bitmap("search"));
 }
 
 
+void SearchCtrl::OnSelect(wxCommandEvent& event)
+{
+    prevent_update = true;
+
+    int selection = event.GetSelection();
+    if (selection >= 0)
+        wxGetApp().sidebar().jump_to_option(selection);
+
+    prevent_update = false;
+
+    comboCtrl->Dismiss();
+}
+
+void SearchCtrl::update_list(std::vector<SearchOptions::Filter>& filters)
+{
+    popupCtrl->Clear();
+    for (const SearchOptions::Filter& item : filters)
+        popupCtrl->Append(item.label);
+}
+
+void SearchCtrl::OnLeftDown(wxEvent &event)
+{
+    const wxString& str = comboCtrl->GetValue();
+    if (!str.IsEmpty() && str == default_string) {
+        prevent_update = true;
+        comboCtrl->SetValue("");
+        prevent_update = false;
+    }
+    event.Skip();
+}
+
 }}    // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 523457976..2fa460b01 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -9,6 +9,8 @@
 #include <wx/popupwin.h>
 #include <wx/listctrl.h>
 
+#include <wx/combo.h>
+
 #include "Preset.hpp"
 #include "wxExtensions.hpp"
 
@@ -147,19 +149,75 @@ private:
     void OnSelect(wxListEvent& event);
 };
 
+
+
+
+
+class SearchComboPopup : public wxListBox, public wxComboPopup
+{
+public:
+    // Initialize member variables
+    virtual void Init(){}
+
+    // Create popup control
+    virtual bool Create(wxWindow* parent)
+    {
+        return wxListBox::Create(parent, 1, wxPoint(0, 0), wxDefaultSize);
+    }
+    // Return pointer to the created control
+    virtual wxWindow* GetControl() { return this; }
+    // Translate string into a list selection
+    virtual void SetStringValue(const wxString& s)
+    {
+        int n = wxListBox::FindString(s);
+        if (n >= 0 && n < wxListBox::GetCount())
+            wxListBox::Select(n);
+
+        // save a combo control's string
+        m_input_string = s;
+    }
+    // Get list selection as a string
+    virtual wxString GetStringValue() const
+    {
+        // we shouldn't change a combo control's string
+        return m_input_string;
+    }
+    // Do mouse hot-tracking (which is typical in list popups)
+    void OnMouseMove(wxMouseEvent& event)
+    {
+        // TODO: Move selection to cursor
+    }
+    // On mouse left up, set the value and close the popup
+    void OnMouseClick(wxMouseEvent& WXUNUSED(event))
+    {
+         // TODO: Send event as well
+        Dismiss();
+    }
+protected:
+    wxString m_input_string;
+};
+
+
 class SearchCtrl
 {
-//    wxWindow*           parent      {nullptr};
     wxBoxSizer*         box_sizer   {nullptr};
     wxTextCtrl*         search_line {nullptr};
     ScalableButton*     search_btn  {nullptr};
     PopupSearchList*    popup_win   {nullptr};
 
+
     bool                prevent_update{ false };
+    wxString            default_string;
 
     void PopupList(wxCommandEvent& event);
     void OnInputText(wxCommandEvent& event);
 
+    wxComboCtrl*        comboCtrl {nullptr};
+    SearchComboPopup*   popupCtrl {nullptr};
+
+    void OnSelect(wxCommandEvent& event);
+    void OnLeftDown(wxEvent& event);
+
 public:
     SearchCtrl(wxWindow* parent);
     ~SearchCtrl();
@@ -168,8 +226,12 @@ public:
 
     void		set_search_line(const std::string& search_line);
     void        msw_rescale();
+
+    void        update_list(std::vector<SearchOptions::Filter>& filters);
 };
 
+
+
 }}
 
 #endif //slic3r_SearchComboBox_hpp_

From b81c774ee5c2e60486c8643c5e54e694fbe48861 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Sun, 5 Apr 2020 13:20:27 +0200
Subject: [PATCH 15/55] Fixed build on OSX

---
 src/slic3r/GUI/OptionsGroup.hpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp
index 5b4879f67..f5b181f60 100644
--- a/src/slic3r/GUI/OptionsGroup.hpp
+++ b/src/slic3r/GUI/OptionsGroup.hpp
@@ -205,12 +205,12 @@ public:
                 const wxPen pen = wxPen(wxColour(250, 10, 10), 2, wxPENSTYLE_SOLID);
                 dc.SetPen(pen);
                 dc.SetBrush(wxBrush(wxColour(250, 0, 0), wxBRUSHSTYLE_SOLID));
-                dc.DrawRectangle(5, 5, sz.x - 5, sz.y - 5);
-
+                dc.DrawRectangle(25, 25, sz.x - 25, sz.y - 25);
+/*
                 HDC hdc = GetHdcOf(dc);
                 RECT dim = { 5, 5, sz.x - 5, sz.y - 5 };
                 ::FillRect(hdc, &dim, GetHbrushOf(wxBrush(wxColour( 0, 250,0), wxBRUSHSTYLE_SOLID)));
-
+*/
             });
     }
 

From 2317437edec245acebf257adf26c59652dbd5db5 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Sun, 5 Apr 2020 22:11:45 +0200
Subject: [PATCH 16/55] Next Experiment

---
 src/slic3r/GUI/Search.cpp | 38 ++++++++++++++++++++++++-----
 src/slic3r/GUI/Search.hpp | 50 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 81 insertions(+), 7 deletions(-)

diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 6cdfc1905..4990b17ee 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -464,16 +464,26 @@ SearchCtrl::SearchCtrl(wxWindow* parent)
 
     comboCtrl = new wxComboCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER);
     comboCtrl->UseAltPopupWindow();
-    popupCtrl = new SearchComboPopup();
 
-    // It is important to call SetPopupControl() as soon as possible
-    comboCtrl->SetPopupControl(popupCtrl);
     wxBitmap bmp_norm = create_scaled_bitmap("search_gray");
     wxBitmap bmp_hov = create_scaled_bitmap("search");
     comboCtrl->SetButtonBitmaps(bmp_norm, true, bmp_hov, bmp_hov, bmp_norm);
+
+//    popupListBox = new SearchComboPopup();
+    popupListCtrl = new SearchComboPopup_();
+
+    // It is important to call SetPopupControl() as soon as possible
+//    comboCtrl->SetPopupControl(popupListBox);
+    comboCtrl->SetPopupControl(popupListCtrl);
+
     box_sizer->Add(comboCtrl, 0, wxALIGN_CENTER_VERTICAL);
 
-    popupCtrl->Bind(wxEVT_LISTBOX, &SearchCtrl::OnSelect, this);
+//    popupListBox->Bind(wxEVT_LISTBOX, &SearchCtrl::OnSelect, this);
+//    popupListCtrl->Bind(wxEVT_LIST_ITEM_SELECTED, &SearchCtrl::OnSelectCtrl, this);
+    popupListCtrl->Bind(wxEVT_LIST_ITEM_SELECTED, [](wxListEvent& event)
+    {
+        int i=0;
+    });
 
     comboCtrl->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
     comboCtrl->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
@@ -557,11 +567,27 @@ void SearchCtrl::OnSelect(wxCommandEvent& event)
     comboCtrl->Dismiss();
 }
 
+void SearchCtrl::OnSelectCtrl(wxListEvent& event)
+{
+    prevent_update = true;
+
+    int selection = event.GetIndex();
+    if (selection >= 0)
+        wxGetApp().sidebar().jump_to_option(selection);
+
+    prevent_update = false;
+
+    comboCtrl->Dismiss();
+}
+
 void SearchCtrl::update_list(std::vector<SearchOptions::Filter>& filters)
 {
-    popupCtrl->Clear();
+/*    popupListBox->Clear();
     for (const SearchOptions::Filter& item : filters)
-        popupCtrl->Append(item.label);
+        popupListBox->Append(item.label);*/
+    popupListCtrl->DeleteAllItems();
+    for (const SearchOptions::Filter& item : filters)
+        popupListCtrl->InsertItem(popupListCtrl->GetItemCount(), item.label);
 }
 
 void SearchCtrl::OnLeftDown(wxEvent &event)
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 2fa460b01..720171018 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -198,6 +198,52 @@ protected:
 };
 
 
+
+class SearchComboPopup_ : public wxListCtrl, public wxComboPopup
+{
+public:
+    // Initialize member variables
+    virtual void Init() {}
+
+    // Create popup control
+    virtual bool Create(wxWindow* parent)
+    {
+        return wxListCtrl::Create(parent, 1, wxPoint(0, 0), wxDefaultSize, wxLC_LIST | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
+    }
+    // Return pointer to the created control
+    virtual wxWindow* GetControl() { return this; }
+    // Translate string into a list selection
+    virtual void SetStringValue(const wxString& s)
+    {
+        int n = wxListCtrl::FindItem(0, s);
+        if (n >= 0 && n < wxListCtrl::GetItemCount())
+            wxListCtrl::SetItemState(n, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+
+        // save a combo control's string
+        m_input_string = s;
+    }
+    // Get list selection as a string
+    virtual wxString GetStringValue() const
+    {
+        // we shouldn't change a combo control's string
+        return m_input_string;
+    }
+    // Do mouse hot-tracking (which is typical in list popups)
+    void OnMouseMove(wxMouseEvent& event)
+    {
+        // TODO: Move selection to cursor
+    }
+    // On mouse left up, set the value and close the popup
+    void OnMouseClick(wxMouseEvent& WXUNUSED(event))
+    {
+        // TODO: Send event as well
+        Dismiss();
+    }
+protected:
+    wxString m_input_string;
+};
+
+
 class SearchCtrl
 {
     wxBoxSizer*         box_sizer   {nullptr};
@@ -213,10 +259,12 @@ class SearchCtrl
     void OnInputText(wxCommandEvent& event);
 
     wxComboCtrl*        comboCtrl {nullptr};
-    SearchComboPopup*   popupCtrl {nullptr};
+    SearchComboPopup*   popupListBox {nullptr};
+    SearchComboPopup_*   popupListCtrl {nullptr};
 
     void OnSelect(wxCommandEvent& event);
     void OnLeftDown(wxEvent& event);
+    void OnSelectCtrl(wxListEvent& event);
 
 public:
     SearchCtrl(wxWindow* parent);

From 752083cbe68c053d88b90981d79050eae27818f7 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 7 Apr 2020 12:09:58 +0200
Subject: [PATCH 17/55] Implemented blinking icon to highlight a searched field

---
 src/slic3r/GUI/Field.cpp        |  3 +++
 src/slic3r/GUI/Field.hpp        | 16 ++++++++++++++++
 src/slic3r/GUI/OptionsGroup.cpp |  1 +
 src/slic3r/GUI/OptionsGroup.hpp | 17 -----------------
 src/slic3r/GUI/Tab.cpp          | 12 +++++++++++-
 src/slic3r/GUI/Tab.hpp          | 28 ++++++++++++++++++++++++++++
 6 files changed, 59 insertions(+), 18 deletions(-)

diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index a1a6583bc..d69fd6c06 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -57,6 +57,9 @@ void Field::PostInitialize()
     m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_initial_value(); }));
 	m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_sys_value(); }));
 
+	m_attention_bmp		= ScalableBitmap(m_parent, "error_tick_f");
+	m_find_image		= new wxStaticBitmap(m_parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, m_attention_bmp.bmp().GetSize());
+
 	switch (m_opt.type)
 	{
 	case coPercents:
diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp
index 7f7ee8ed8..73529cf31 100644
--- a/src/slic3r/GUI/Field.hpp
+++ b/src/slic3r/GUI/Field.hpp
@@ -191,6 +191,19 @@ public:
 		return false;
 	}
 
+	void	invalidate_attention_bmp() const {
+        m_find_image->SetBitmap(wxNullBitmap);
+    }
+
+	void	activate_attention_bmp() const {
+		m_find_image->SetBitmap(m_attention_bmp.bmp());
+	}
+
+	void	blink_attention_bmp() const {
+		bool is_shown = m_find_image->IsShown();
+		m_find_image->Show(!is_shown);
+	}
+
 	bool	set_label_colour_force(const wxColour *clr) {
 		if (m_Label == nullptr) return false;
 		m_Label->SetForegroundColour(*clr);
@@ -240,6 +253,9 @@ protected:
     const ScalableBitmap*   m_undo_to_sys_bitmap = nullptr;
 	const wxString*		    m_undo_to_sys_tooltip = nullptr;
 
+	ScalableBitmap			m_attention_bmp;
+	wxStaticBitmap*			m_find_image{ nullptr };
+
 	wxStaticText*		m_Label = nullptr;
 	// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
 	const wxColour*		m_label_color = nullptr;
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index 207d42b5b..ca26dfe80 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -110,6 +110,7 @@ void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& fiel
 
 	sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
 	sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
+    sizer->Add(field->m_find_image, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 2);
 }
 
 void OptionsGroup::append_line(const Line& line, wxStaticText**	full_Label/* = nullptr*/) {
diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp
index f5b181f60..6c7c8c10d 100644
--- a/src/slic3r/GUI/OptionsGroup.hpp
+++ b/src/slic3r/GUI/OptionsGroup.hpp
@@ -195,23 +195,6 @@ public:
         sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5);
 #endif /* __WXGTK__ */
 
-
-
-        if (stb)
-            stb->Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {
-                evt.Skip();
-                const wxSize sz = stb->GetSize();
-                wxPaintDC dc(stb);
-                const wxPen pen = wxPen(wxColour(250, 10, 10), 2, wxPENSTYLE_SOLID);
-                dc.SetPen(pen);
-                dc.SetBrush(wxBrush(wxColour(250, 0, 0), wxBRUSHSTYLE_SOLID));
-                dc.DrawRectangle(25, 25, sz.x - 25, sz.y - 25);
-/*
-                HDC hdc = GetHdcOf(dc);
-                RECT dim = { 5, 5, sz.x - 5, sz.y - 5 };
-                ::FillRect(hdc, &dim, GetHbrushOf(wxBrush(wxColour( 0, 250,0), wxBRUSHSTYLE_SOLID)));
-*/
-            });
     }
 
     wxGridSizer*        get_grid_sizer() { return m_grid_sizer; }
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 17bb68d02..333840986 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -69,6 +69,13 @@ Tab::Tab(wxNotebook* parent, const wxString& title, Preset::Type type) :
                 page->layout_valid = false;
         evt.Skip();
     }));
+
+    this->m_highlighting_timer.SetOwner(this, 0);
+    this->Bind(wxEVT_TIMER, [this](wxTimerEvent&)
+    {
+        if (!m_highlighter.blink())
+            m_highlighting_timer.Stop();
+    });
 }
 
 void Tab::set_type()
@@ -992,8 +999,11 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
         tap_panel->SetSelection(page_id);
 
     // focused selected field
-    if (field)
+    if (field) {
         field->getWindow()->SetFocus();
+        m_highlighting_timer.Start(500, false);
+        m_highlighter.init(field);
+    }
 }
 
 
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index fe683b49f..4a6ab7c19 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -224,6 +224,34 @@ protected:
     bool                m_completed { false };
     ConfigOptionMode    m_mode = comExpert; // to correct first Tab update_visibility() set mode to Expert
 
+	wxTimer             m_highlighting_timer;
+	struct {
+	    Field*	field {nullptr};
+		int		blink_counter {0};
+
+		void init(Field* f)
+		{
+		    field = f;
+			field->activate_attention_bmp();
+		}
+
+		void invalidate()
+		{
+			field->invalidate_attention_bmp();
+		    field = nullptr;
+			blink_counter = 0;
+		}
+
+		bool blink()
+		{
+			field->blink_attention_bmp();
+			if ((++blink_counter) == 5)
+			    invalidate();
+
+			return blink_counter != 0;
+		}
+	} m_highlighter;
+
 public:
 	PresetBundle*		m_preset_bundle;
 	bool				m_show_btn_incompatible_presets = false;

From cd13356b6dd388cfc9488a2b6ef3e2150601cfd7 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 7 Apr 2020 16:22:03 +0200
Subject: [PATCH 18/55] Code cleaning

---
 resources/icons/attention.svg | 12 +++++
 src/slic3r/GUI/Field.cpp      |  2 +-
 src/slic3r/GUI/Field.hpp      |  1 +
 src/slic3r/GUI/Plater.cpp     |  6 +--
 src/slic3r/GUI/Search.cpp     | 84 +++++++++++++++++------------------
 src/slic3r/GUI/Search.hpp     | 58 +++---------------------
 6 files changed, 64 insertions(+), 99 deletions(-)
 create mode 100644 resources/icons/attention.svg

diff --git a/resources/icons/attention.svg b/resources/icons/attention.svg
new file mode 100644
index 000000000..934bd3b41
--- /dev/null
+++ b/resources/icons/attention.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="attention">
+	<path fill="#ED0000" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>	
+	
+    <path fill="none" stroke="#ED0000" stroke-linecap="round" stroke-width="3" d="M8 4 L8 8" />
+		
+	<circle fill="#ED0000" cx="8" cy="12" r="1.5"/>	
+</g>
+</svg>
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index d69fd6c06..dfe561caf 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -57,7 +57,7 @@ void Field::PostInitialize()
     m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_initial_value(); }));
 	m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_sys_value(); }));
 
-	m_attention_bmp		= ScalableBitmap(m_parent, "error_tick_f");
+	m_attention_bmp		= ScalableBitmap(m_parent, "attention");
 	m_find_image		= new wxStaticBitmap(m_parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, m_attention_bmp.bmp().GetSize());
 
 	switch (m_opt.type)
diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp
index 73529cf31..d716be25c 100644
--- a/src/slic3r/GUI/Field.hpp
+++ b/src/slic3r/GUI/Field.hpp
@@ -193,6 +193,7 @@ public:
 
 	void	invalidate_attention_bmp() const {
         m_find_image->SetBitmap(wxNullBitmap);
+		m_find_image->Show();
     }
 
 	void	activate_attention_bmp() const {
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index acf762bfb..3d2f6052f 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1093,8 +1093,8 @@ void Sidebar::msw_rescale()
 
 void Sidebar::apply_search_filter()
 {
-    p->search_list.apply_filters(p->search_line);
-    apply_search_line_on_tabs();
+    if (p->search_list.apply_filters(p->search_line))
+        apply_search_line_on_tabs();
 }
 
 void Sidebar::jump_to_option(size_t selected)
@@ -1387,7 +1387,7 @@ void Sidebar::apply_search_line_on_tabs()
 void Sidebar::update_search_list()
 {
     p->search_list.init(get_search_inputs(m_mode));
-    apply_search_line_on_tabs();
+//    apply_search_line_on_tabs();
 }
 
 void Sidebar::update_mode()
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 4990b17ee..95bf2da3f 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -163,8 +163,11 @@ static void clear_marked_string(wxString& str)
     str.Replace(delete_string, wxEmptyString, true);
 }
 
-void SearchOptions::apply_filters(const std::string& search)
+bool SearchOptions::apply_filters(const std::string& search, bool force/* = false*/)
 {
+    if (search_line == search && !force)
+        return false;
+
     clear_filters();
 
     bool full_list = search.empty();
@@ -189,6 +192,9 @@ void SearchOptions::apply_filters(const std::string& search)
 
     if (!full_list)
         sort_filters();
+
+    search_line = search;
+    return true;
 }
 
 void SearchOptions::init(std::vector<SearchInput> input_values)
@@ -198,7 +204,7 @@ void SearchOptions::init(std::vector<SearchInput> input_values)
         append_options(i.config, i.type, i.mode);
     sort_options();
 
-    apply_filters("");
+    apply_filters("", true);
 }
 
 const SearchOptions::Option& SearchOptions::get_option(size_t pos_in_filter) const
@@ -469,27 +475,21 @@ SearchCtrl::SearchCtrl(wxWindow* parent)
     wxBitmap bmp_hov = create_scaled_bitmap("search");
     comboCtrl->SetButtonBitmaps(bmp_norm, true, bmp_hov, bmp_hov, bmp_norm);
 
-//    popupListBox = new SearchComboPopup();
-    popupListCtrl = new SearchComboPopup_();
+    popupListBox = new SearchComboPopup();
 
     // It is important to call SetPopupControl() as soon as possible
-//    comboCtrl->SetPopupControl(popupListBox);
-    comboCtrl->SetPopupControl(popupListCtrl);
+    comboCtrl->SetPopupControl(popupListBox);
 
     box_sizer->Add(comboCtrl, 0, wxALIGN_CENTER_VERTICAL);
 
-//    popupListBox->Bind(wxEVT_LISTBOX, &SearchCtrl::OnSelect, this);
-//    popupListCtrl->Bind(wxEVT_LIST_ITEM_SELECTED, &SearchCtrl::OnSelectCtrl, this);
-    popupListCtrl->Bind(wxEVT_LIST_ITEM_SELECTED, [](wxListEvent& event)
-    {
-        int i=0;
-    });
+    popupListBox->Bind(wxEVT_LISTBOX,           &SearchCtrl::OnSelect, this);
+    popupListBox->Bind(wxEVT_LEFT_DOWN,         &SearchCtrl::OnLeftDownInPopup, this);
 
     comboCtrl->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
     comboCtrl->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
     comboCtrl->Bind(wxEVT_COMBOBOX_DROPDOWN,    &SearchCtrl::PopupList, this);
 
-    comboCtrl->GetTextCtrl()->Bind(wxEVT_LEFT_DOWN,    &SearchCtrl::OnLeftDown, this);
+    comboCtrl->GetTextCtrl()->Bind(wxEVT_LEFT_UP,    &SearchCtrl::OnLeftUpInTextCtrl, this);
 }
 
 SearchCtrl::~SearchCtrl()
@@ -507,14 +507,21 @@ void SearchCtrl::OnInputText(wxCommandEvent& )
     if (prevent_update)
         return;
 
+    comboCtrl->GetTextCtrl()->SetInsertionPointEnd();
+
+    wxString input_string = comboCtrl->GetValue();
+    if (input_string == default_string)
+        input_string.Clear();
+
     std::string& search_str = wxGetApp().sidebar().get_search_line();
 //    search_str = into_u8(search_line->GetValue());
-    search_str = into_u8(comboCtrl->GetValue());
+    wxGetApp().sidebar().get_search_line() = into_u8(input_string);
+
+    editing = true;
     wxGetApp().sidebar().apply_search_filter();
+    editing = false;
 
 //    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
-
-//    update_list(wxGetApp().sidebar().get_search_list().filters);
 }
 
 void SearchCtrl::PopupList(wxCommandEvent& e)
@@ -536,7 +543,7 @@ void SearchCtrl::set_search_line(const std::string& line)
 {
     prevent_update = true;
 //    search_line->SetValue(line.empty() ? _L("Type here to search") : from_u8(line));
-    comboCtrl->SetText(line.empty() ? default_string : from_u8(line));
+    comboCtrl->SetValue(line.empty() && !editing ? default_string : from_u8(line));
     prevent_update = false;
 
 }
@@ -549,7 +556,6 @@ void SearchCtrl::msw_rescale()
     // Set rescaled size
     search_btn->msw_rescale();
 
-
     comboCtrl->SetButtonBitmaps(create_scaled_bitmap("search"));
 }
 
@@ -567,37 +573,31 @@ void SearchCtrl::OnSelect(wxCommandEvent& event)
     comboCtrl->Dismiss();
 }
 
-void SearchCtrl::OnSelectCtrl(wxListEvent& event)
-{
-    prevent_update = true;
-
-    int selection = event.GetIndex();
-    if (selection >= 0)
-        wxGetApp().sidebar().jump_to_option(selection);
-
-    prevent_update = false;
-
-    comboCtrl->Dismiss();
-}
-
 void SearchCtrl::update_list(std::vector<SearchOptions::Filter>& filters)
 {
-/*    popupListBox->Clear();
+    if (popupListBox->GetCount() == filters.size() &&
+        popupListBox->GetString(0) == filters[0].label &&
+        popupListBox->GetString(popupListBox->GetCount()-1) == filters[filters.size()-1].label)
+        return;
+
+    popupListBox->Clear();
     for (const SearchOptions::Filter& item : filters)
-        popupListBox->Append(item.label);*/
-    popupListCtrl->DeleteAllItems();
-    for (const SearchOptions::Filter& item : filters)
-        popupListCtrl->InsertItem(popupListCtrl->GetItemCount(), item.label);
+        popupListBox->Append(item.label);
 }
 
-void SearchCtrl::OnLeftDown(wxEvent &event)
+void SearchCtrl::OnLeftUpInTextCtrl(wxEvent &event)
 {
-    const wxString& str = comboCtrl->GetValue();
-    if (!str.IsEmpty() && str == default_string) {
-        prevent_update = true;
+    if (comboCtrl->GetValue() == default_string)
         comboCtrl->SetValue("");
-        prevent_update = false;
-    }
+
+    event.Skip();
+}
+
+void SearchCtrl::OnLeftDownInPopup(wxEvent &event)
+{
+    wxPoint pt = wxGetMousePosition() - popupListBox->GetScreenPosition();
+    int selected_item = popupListBox->HitTest(pt);
+
     event.Skip();
 }
 
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 720171018..a27a10380 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -28,6 +28,7 @@ struct SearchInput
 
 class SearchOptions
 {
+    std::string         search_line;
 public:
     struct Option {
         bool operator<(const Option& other) const { return other.label > this->label; }
@@ -65,7 +66,7 @@ public:
     void init(std::vector<SearchInput> input_values);
 
     void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
-    void apply_filters(const std::string& search);
+    bool apply_filters(const std::string& search, bool force = false);
 
     void sort_options() {
         std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) {
@@ -151,8 +152,6 @@ private:
 
 
 
-
-
 class SearchComboPopup : public wxListBox, public wxComboPopup
 {
 public:
@@ -197,53 +196,6 @@ protected:
     wxString m_input_string;
 };
 
-
-
-class SearchComboPopup_ : public wxListCtrl, public wxComboPopup
-{
-public:
-    // Initialize member variables
-    virtual void Init() {}
-
-    // Create popup control
-    virtual bool Create(wxWindow* parent)
-    {
-        return wxListCtrl::Create(parent, 1, wxPoint(0, 0), wxDefaultSize, wxLC_LIST | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
-    }
-    // Return pointer to the created control
-    virtual wxWindow* GetControl() { return this; }
-    // Translate string into a list selection
-    virtual void SetStringValue(const wxString& s)
-    {
-        int n = wxListCtrl::FindItem(0, s);
-        if (n >= 0 && n < wxListCtrl::GetItemCount())
-            wxListCtrl::SetItemState(n, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
-
-        // save a combo control's string
-        m_input_string = s;
-    }
-    // Get list selection as a string
-    virtual wxString GetStringValue() const
-    {
-        // we shouldn't change a combo control's string
-        return m_input_string;
-    }
-    // Do mouse hot-tracking (which is typical in list popups)
-    void OnMouseMove(wxMouseEvent& event)
-    {
-        // TODO: Move selection to cursor
-    }
-    // On mouse left up, set the value and close the popup
-    void OnMouseClick(wxMouseEvent& WXUNUSED(event))
-    {
-        // TODO: Send event as well
-        Dismiss();
-    }
-protected:
-    wxString m_input_string;
-};
-
-
 class SearchCtrl
 {
     wxBoxSizer*         box_sizer   {nullptr};
@@ -254,17 +206,17 @@ class SearchCtrl
 
     bool                prevent_update{ false };
     wxString            default_string;
+    bool                editing {false};
 
     void PopupList(wxCommandEvent& event);
     void OnInputText(wxCommandEvent& event);
 
     wxComboCtrl*        comboCtrl {nullptr};
     SearchComboPopup*   popupListBox {nullptr};
-    SearchComboPopup_*   popupListCtrl {nullptr};
 
     void OnSelect(wxCommandEvent& event);
-    void OnLeftDown(wxEvent& event);
-    void OnSelectCtrl(wxListEvent& event);
+    void OnLeftDownInPopup(wxEvent& event);
+    void OnLeftUpInTextCtrl(wxEvent& event);
 
 public:
     SearchCtrl(wxWindow* parent);

From dcdafb6208401462d24e244a9d398cab05d6a6e5 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 7 Apr 2020 19:09:33 +0200
Subject: [PATCH 19/55] Implemented OnMouseMove and OnMouseClick for
 PopupSearchList

---
 src/slic3r/GUI/Search.cpp | 27 +++++++++++++++------------
 src/slic3r/GUI/Search.hpp | 19 ++++++++++++++++---
 2 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 95bf2da3f..eb7c98d87 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -482,8 +482,8 @@ SearchCtrl::SearchCtrl(wxWindow* parent)
 
     box_sizer->Add(comboCtrl, 0, wxALIGN_CENTER_VERTICAL);
 
-    popupListBox->Bind(wxEVT_LISTBOX,           &SearchCtrl::OnSelect, this);
-    popupListBox->Bind(wxEVT_LEFT_DOWN,         &SearchCtrl::OnLeftDownInPopup, this);
+//    popupListBox->Bind(wxEVT_LEFT_DOWN,         &SearchCtrl::OnLeftDownInPopup, this);
+    popupListBox->Bind(wxEVT_LISTBOX,           &SearchCtrl::OnSelect,          this);
 
     comboCtrl->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
     comboCtrl->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
@@ -559,18 +559,21 @@ void SearchCtrl::msw_rescale()
     comboCtrl->SetButtonBitmaps(create_scaled_bitmap("search"));
 }
 
+void SearchCtrl::select(int selection)
+{
+    if (selection < 0)
+        return;
+
+    prevent_update = true;
+    wxGetApp().sidebar().jump_to_option(selection);
+    prevent_update = false;
+
+//    comboCtrl->Dismiss();
+}
 
 void SearchCtrl::OnSelect(wxCommandEvent& event)
 {
-    prevent_update = true;
-
-    int selection = event.GetSelection();
-    if (selection >= 0)
-        wxGetApp().sidebar().jump_to_option(selection);
-
-    prevent_update = false;
-
-    comboCtrl->Dismiss();
+    select(event.GetSelection());
 }
 
 void SearchCtrl::update_list(std::vector<SearchOptions::Filter>& filters)
@@ -596,7 +599,7 @@ void SearchCtrl::OnLeftUpInTextCtrl(wxEvent &event)
 void SearchCtrl::OnLeftDownInPopup(wxEvent &event)
 {
     wxPoint pt = wxGetMousePosition() - popupListBox->GetScreenPosition();
-    int selected_item = popupListBox->HitTest(pt);
+    select(popupListBox->HitTest(pt));
 
     event.Skip();
 }
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index a27a10380..e695e4ecf 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -156,7 +156,11 @@ class SearchComboPopup : public wxListBox, public wxComboPopup
 {
 public:
     // Initialize member variables
-    virtual void Init(){}
+    virtual void Init()
+    {
+        this->Bind(wxEVT_MOTION, &SearchComboPopup::OnMouseMove, this);
+        this->Bind(wxEVT_LEFT_UP, &SearchComboPopup::OnMouseClick, this);
+    }
 
     // Create popup control
     virtual bool Create(wxWindow* parent)
@@ -184,12 +188,20 @@ public:
     // Do mouse hot-tracking (which is typical in list popups)
     void OnMouseMove(wxMouseEvent& event)
     {
-        // TODO: Move selection to cursor
+        wxPoint pt = wxGetMousePosition() - this->GetScreenPosition();
+        int selection = this->HitTest(pt);
+        wxListBox::Select(selection);
     }
     // On mouse left up, set the value and close the popup
     void OnMouseClick(wxMouseEvent& WXUNUSED(event))
     {
-         // TODO: Send event as well
+        int selection = wxListBox::GetSelection();
+        SetSelection(wxNOT_FOUND);
+        wxCommandEvent event(wxEVT_LISTBOX, GetId());
+        event.SetInt(selection);
+        event.SetEventObject(this);
+        ProcessEvent(event);
+
         Dismiss();
     }
 protected:
@@ -226,6 +238,7 @@ public:
 
     void		set_search_line(const std::string& search_line);
     void        msw_rescale();
+    void        select(int selection);
 
     void        update_list(std::vector<SearchOptions::Filter>& filters);
 };

From 05f4b7aa60019f09ca1cb55ee6ccd1ccf8e370d9 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 7 Apr 2020 20:34:09 +0200
Subject: [PATCH 20/55] Search: Code cleaning

---
 src/slic3r/GUI/Search.cpp | 333 +++-----------------------------------
 src/slic3r/GUI/Search.hpp |  84 +---------
 src/slic3r/GUI/Tab.cpp    |  32 +++-
 src/slic3r/GUI/Tab.hpp    |  29 +---
 4 files changed, 59 insertions(+), 419 deletions(-)

diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index eb7c98d87..e1ca8865e 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -204,7 +204,7 @@ void SearchOptions::init(std::vector<SearchInput> input_values)
         append_options(i.config, i.type, i.mode);
     sort_options();
 
-    apply_filters("", true);
+    apply_filters(search_line, true);
 }
 
 const SearchOptions::Option& SearchOptions::get_option(size_t pos_in_filter) const
@@ -212,294 +212,34 @@ const SearchOptions::Option& SearchOptions::get_option(size_t pos_in_filter) con
     assert(pos_in_filter != size_t(-1) && filters[pos_in_filter].option_idx != size_t(-1));
     return options[filters[pos_in_filter].option_idx];
 }
-/*
-SearchComboBox::SearchComboBox(wxWindow *parent, SearchOptions& search_list) :
-wxBitmapComboBox(parent, wxID_ANY, _(L("Type here to search")) + dots, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1)),
-    em_unit(wxGetApp().em_unit()),
-    search_list(search_list)
-{
-    SetFont(wxGetApp().normal_font());
-    default_search_line = search_line = _(L("Type here to search")) + dots;
-    bmp = ScalableBitmap(this, "search");
-
-    Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
-        auto selected_item = this->GetSelection();
-        if (selected_item < 0)
-            return;
-
-        wxGetApp().sidebar().jump_to_option(selected_item);
-
-        return;
-        SearchOptions::Option* opt = reinterpret_cast<SearchOptions::Option*>(this->GetClientData(selected_item));
-        wxGetApp().get_tab(opt->type)->activate_option(opt->opt_key, opt->category);
-
-        evt.StopPropagation();
-
-        SuppressUpdate su(this);
-        this->SetValue(search_line);
-    });
-
-    Bind(wxEVT_TEXT, [this](wxCommandEvent &e) {
-/*        if (prevent_update)
-            return;
-
-        if (this->IsTextEmpty())
-        {
-            return;
-        }
-
- * /       if (search_line != this->GetValue()) {
-            std::string& search_str = wxGetApp().sidebar().get_search_line();
-            search_str = into_u8(this->GetValue());
-            wxGetApp().sidebar().apply_search_filter();
-            update_combobox();
-            search_line = this->GetValue();
-        }
-
-        e.Skip();
-    });
-}
-
-SearchComboBox::~SearchComboBox()
-{
-}
-
-void SearchComboBox::msw_rescale()
-{
-    em_unit = wxGetApp().em_unit();
-
-    wxSize size = wxSize(25 * em_unit, -1);
-
-    // Set rescaled min height to correct layout
-    this->SetMinSize(size);
-    // Set rescaled size
-    this->SetSize(size);
-
-    update_combobox();
-}
-
-void SearchComboBox::init(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
-{
-    search_list.clear_options();
-    search_list.append_options(config, type, mode);
-    search_list.sort_options();
-
-    update_combobox();
-}
-
-void SearchComboBox::init(std::vector<SearchInput> input_values)
-{
-    search_list.clear_options();
-    for (auto i : input_values)
-        search_list.append_options(i.config, i.type, i.mode);
-    search_list.sort_options();
-
-    update_combobox();
-}
-
-void SearchComboBox::init(const SearchOptions& new_search_list)
-{
-    search_list = new_search_list;
-
-    update_combobox();
-}
-
-void SearchComboBox::update_combobox()
-{
-    this->Clear();
-    for (const SearchOptions::Filter& item : search_list.filters)
-        append(item.label);
-
-//    SuppressUpdate su(this);
-//    this->SetValue(default_search_line);
-
-    return;
-    wxString search_str = this->GetValue();
-    if (search_str.IsEmpty() || search_str == default_search_line)
-        // add whole options list to the controll
-        append_all_items();
-    else
-        append_items(search_str);
-}
-
-void SearchComboBox::append_all_items()
-{
-    this->Clear();
-    for (const SearchOptions::Option& item : search_list.options)
-        if (!item.label.IsEmpty())
-            append(item.label, (void*)&item);
-
-    SuppressUpdate su(this);
-    this->SetValue(default_search_line);
-}
-
-void SearchComboBox::append_items(const wxString& search)
-{
-    this->Clear();
-/*
-    search_list.apply_filters(search);
-    for (auto filter : search_list.filters) {
-        auto it = std::lower_bound(search_list.options.begin(), search_list.options.end(), SearchOptions::Option{filter.label});
-        if (it != search_list.options.end())
-            append(it->label, (void*)(&(*it)));
-    }
-* /
-
-    for (const SearchOptions::Option& option : search_list.options)
-        if (option.fuzzy_match_simple(search))
-            append(option.label, (void*)&option);
-
-    SuppressUpdate su(this);
-    this->SetValue(search);
-    this->SetInsertionPointEnd();
-}
-*/
-
-//------------------------------------------
-//          PopupSearchList
-//------------------------------------------
-
-PopupSearchList::PopupSearchList(wxWindow* parent) :
-    wxPopupTransientWindow(parent, wxSTAY_ON_TOP| wxBORDER_NONE)
-{
-    panel = new wxPanel(this, wxID_ANY);
-
-    int em_unit = wxGetApp().em_unit();
-
-    search_ctrl = new wxListCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(25 * em_unit, 35 * em_unit), wxLC_NO_HEADER | wxLC_REPORT);
-    search_ctrl->AppendColumn("");
-    search_ctrl->SetColumnWidth(0, 23 * em_unit);
-    search_ctrl->Bind(wxEVT_LIST_ITEM_SELECTED, &PopupSearchList::OnSelect, this);
-
-    wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
-
-    wxTextCtrl *text = new wxTextCtrl(panel, wxID_ANY, "Brrrr");
-    text->Bind(wxEVT_ACTIVATE, [](wxEvent& e) {
-        int i=0; });
-    text->Bind(wxEVT_MOUSE_CAPTURE_CHANGED, [](wxEvent& e) {
-        int i = 0; });
-    text->Bind(wxEVT_LEFT_DOWN, [text](wxEvent& e) {
-        text->SetValue("mrrrrrty"); });
-    text->Bind(wxEVT_TEXT, [text](wxCommandEvent& e) {
-        text->SetSelection(2, 3); });
-    text->Bind(wxEVT_CHAR, [text](wxKeyEvent& e) {
-        text->SetFocus(); });
-
-    topSizer->Add(text, 0, wxEXPAND | wxALL, 2);
-    topSizer->Add(search_ctrl, 0, wxEXPAND | wxALL, 2);
-
-    panel->SetSizer(topSizer);
-    
-    topSizer->Fit(panel);
-    SetClientSize(panel->GetSize());
-}
-
-void PopupSearchList::Popup(wxWindow* WXUNUSED(focus))
-{
-    wxPopupTransientWindow::Popup();
-}
-
-void PopupSearchList::OnDismiss()
-{
-    wxPopupTransientWindow::OnDismiss();
-}
-
-bool PopupSearchList::ProcessLeftDown(wxMouseEvent& event)
-{
-    return wxPopupTransientWindow::ProcessLeftDown(event);
-}
-bool PopupSearchList::Show(bool show)
-{
-    return wxPopupTransientWindow::Show(show);
-}
-
-void PopupSearchList::OnSize(wxSizeEvent& event)
-{
-    event.Skip();
-}
-
-void PopupSearchList::OnSetFocus(wxFocusEvent& event)
-{
-    event.Skip();
-}
-
-void PopupSearchList::OnKillFocus(wxFocusEvent& event)
-{
-    event.Skip();
-}
-
-void PopupSearchList::OnSelect(wxListEvent& event)
-{
-    int selection = event.GetIndex();
-    if (selection>=0)
-        wxGetApp().sidebar().jump_to_option(selection);
-
-    OnDismiss();
-}
-
-void PopupSearchList::update_list(std::vector<SearchOptions::Filter>& filters)
-{
-    search_ctrl->DeleteAllItems();
-    for (const SearchOptions::Filter& item : filters)
-        search_ctrl->InsertItem(search_ctrl->GetItemCount(), item.label);
-}
 
 
 //------------------------------------------
 //          SearchCtrl
 //------------------------------------------
 
-SearchCtrl::SearchCtrl(wxWindow* parent)
+SearchCtrl::SearchCtrl(wxWindow* parent) :
+    wxComboCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER)
 {
-//    popup_win = new PopupSearchList(parent);
-    box_sizer = new wxBoxSizer(wxHORIZONTAL);
- /*
-    search_line = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER);
-    search_line->Bind(wxEVT_TEXT, &SearchCtrl::OnInputText, this);
-    search_line->Bind(wxEVT_TEXT_ENTER, &SearchCtrl::PopupList, this);
-    
-    search_btn  = new ScalableButton(parent, wxID_ANY, "search");
-    search_btn->Bind(wxEVT_BUTTON, &SearchCtrl::PopupList, this);
-
-    box_sizer->Add(search_line, 0, wxALIGN_CENTER_VERTICAL);
-    box_sizer->AddSpacer(5);
-    box_sizer->Add(search_btn, 0, wxALIGN_CENTER_VERTICAL);
-
-*/
     default_string = _L("Type here to search");
 
-    comboCtrl = new wxComboCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER);
-    comboCtrl->UseAltPopupWindow();
+    this->UseAltPopupWindow();
 
     wxBitmap bmp_norm = create_scaled_bitmap("search_gray");
     wxBitmap bmp_hov = create_scaled_bitmap("search");
-    comboCtrl->SetButtonBitmaps(bmp_norm, true, bmp_hov, bmp_hov, bmp_norm);
+    this->SetButtonBitmaps(bmp_norm, true, bmp_hov, bmp_hov, bmp_norm);
 
     popupListBox = new SearchComboPopup();
 
     // It is important to call SetPopupControl() as soon as possible
-    comboCtrl->SetPopupControl(popupListBox);
+    this->SetPopupControl(popupListBox);
 
-    box_sizer->Add(comboCtrl, 0, wxALIGN_CENTER_VERTICAL);
+    this->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
+    this->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
+    this->Bind(wxEVT_COMBOBOX_DROPDOWN,    &SearchCtrl::PopupList, this);
 
-//    popupListBox->Bind(wxEVT_LEFT_DOWN,         &SearchCtrl::OnLeftDownInPopup, this);
-    popupListBox->Bind(wxEVT_LISTBOX,           &SearchCtrl::OnSelect,          this);
-
-    comboCtrl->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
-    comboCtrl->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
-    comboCtrl->Bind(wxEVT_COMBOBOX_DROPDOWN,    &SearchCtrl::PopupList, this);
-
-    comboCtrl->GetTextCtrl()->Bind(wxEVT_LEFT_UP,    &SearchCtrl::OnLeftUpInTextCtrl, this);
-}
-
-SearchCtrl::~SearchCtrl()
-{
-    if (search_line)
-        search_line->Destroy();
-    if (search_btn)
-        search_btn->Destroy();
-    if (popup_win)
-        popup_win->Destroy();
+    this->GetTextCtrl()->Bind(wxEVT_LEFT_UP,    &SearchCtrl::OnLeftUpInTextCtrl, this);
+    popupListBox->Bind(wxEVT_LISTBOX,           &SearchCtrl::OnSelect,           this);
 }
 
 void SearchCtrl::OnInputText(wxCommandEvent& )
@@ -507,34 +247,21 @@ void SearchCtrl::OnInputText(wxCommandEvent& )
     if (prevent_update)
         return;
 
-    comboCtrl->GetTextCtrl()->SetInsertionPointEnd();
+    this->GetTextCtrl()->SetInsertionPointEnd();
 
-    wxString input_string = comboCtrl->GetValue();
+    wxString input_string = this->GetValue();
     if (input_string == default_string)
         input_string.Clear();
 
-    std::string& search_str = wxGetApp().sidebar().get_search_line();
-//    search_str = into_u8(search_line->GetValue());
     wxGetApp().sidebar().get_search_line() = into_u8(input_string);
 
     editing = true;
     wxGetApp().sidebar().apply_search_filter();
     editing = false;
-
-//    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
 }
 
 void SearchCtrl::PopupList(wxCommandEvent& e)
 {
-/*    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
-
-    wxPoint pos = search_line->ClientToScreen(wxPoint(0, 0));
-    wxSize sz = search_line->GetSize();
-    pos.x -= sz.GetWidth();
-    popup_win->Position(pos, sz);
-
-    popup_win->Popup();
-    */
     update_list(wxGetApp().sidebar().get_search_list().filters);
     e.Skip();
 }
@@ -542,38 +269,30 @@ void SearchCtrl::PopupList(wxCommandEvent& e)
 void SearchCtrl::set_search_line(const std::string& line)
 {
     prevent_update = true;
-//    search_line->SetValue(line.empty() ? _L("Type here to search") : from_u8(line));
-    comboCtrl->SetValue(line.empty() && !editing ? default_string : from_u8(line));
+    this->SetValue(line.empty() && !editing ? default_string : from_u8(line));
     prevent_update = false;
-
 }
 
 void SearchCtrl::msw_rescale()
 {
     wxSize size = wxSize(25 * wxGetApp().em_unit(), -1);
     // Set rescaled min height to correct layout
-    search_line->SetMinSize(size);
-    // Set rescaled size
-    search_btn->msw_rescale();
+    this->SetMinSize(size);
 
-    comboCtrl->SetButtonBitmaps(create_scaled_bitmap("search"));
+    wxBitmap bmp_norm = create_scaled_bitmap("search_gray");
+    wxBitmap bmp_hov = create_scaled_bitmap("search");
+    this->SetButtonBitmaps(bmp_norm, true, bmp_hov, bmp_hov, bmp_norm);
 }
 
-void SearchCtrl::select(int selection)
+void SearchCtrl::OnSelect(wxCommandEvent& event)
 {
+    int selection = event.GetSelection();
     if (selection < 0)
         return;
 
     prevent_update = true;
     wxGetApp().sidebar().jump_to_option(selection);
     prevent_update = false;
-
-//    comboCtrl->Dismiss();
-}
-
-void SearchCtrl::OnSelect(wxCommandEvent& event)
-{
-    select(event.GetSelection());
 }
 
 void SearchCtrl::update_list(std::vector<SearchOptions::Filter>& filters)
@@ -590,16 +309,8 @@ void SearchCtrl::update_list(std::vector<SearchOptions::Filter>& filters)
 
 void SearchCtrl::OnLeftUpInTextCtrl(wxEvent &event)
 {
-    if (comboCtrl->GetValue() == default_string)
-        comboCtrl->SetValue("");
-
-    event.Skip();
-}
-
-void SearchCtrl::OnLeftDownInPopup(wxEvent &event)
-{
-    wxPoint pt = wxGetMousePosition() - popupListBox->GetScreenPosition();
-    select(popupListBox->HitTest(pt));
+    if (this->GetValue() == default_string)
+        this->SetValue("");
 
     event.Skip();
 }
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index e695e4ecf..f083166f5 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -84,72 +84,6 @@ public:
     const Filter& operator[](const size_t pos) const noexcept { return filters[pos]; }
     const Option& get_option(size_t pos_in_filter) const;
 };
-/*
-class SearchComboBox : public wxBitmapComboBox
-{
-    class SuppressUpdate
-    {
-        SearchComboBox* m_cb;
-    public:
-        SuppressUpdate(SearchComboBox* cb) : 
-                m_cb(cb)    { m_cb->prevent_update = true ; }
-        ~SuppressUpdate()   { m_cb->prevent_update = false; }
-    };                                                 
-
-public:
-    SearchComboBox(wxWindow *parent, SearchOptions& search_list);
-    ~SearchComboBox();
-
-    int     append(const wxString& item)                            { return Append(item, bmp.bmp()); }
-    int     append(const wxString& item, void* clientData)          { return Append(item, bmp.bmp(), clientData); }
-    int     append(const wxString& item, wxClientData* clientData)  { return Append(item, bmp.bmp(), clientData); }
-    
-    void    append_all_items();
-    void    append_items(const wxString& search);
-
-    void    msw_rescale();
-
-    void	init(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
-    void    init(std::vector<SearchInput> input_values);
-    void    init(const SearchOptions& new_search_list);
-    void    update_combobox();
-
-
-private:
-    SearchOptions&		search_list;
-    wxString            default_search_line;
-    wxString            search_line;
-
-    int                 em_unit;
-    bool                prevent_update {false};
-
-    ScalableBitmap      bmp;
-};
-*/
-class PopupSearchList : public wxPopupTransientWindow
-{
-public:
-    PopupSearchList(wxWindow* parent);
-    ~PopupSearchList() {}
-
-    // wxPopupTransientWindow virtual methods are all overridden to log them
-    void Popup(wxWindow* focus = NULL) wxOVERRIDE;
-    void OnDismiss() wxOVERRIDE;
-    bool ProcessLeftDown(wxMouseEvent& event) wxOVERRIDE;
-    bool Show(bool show = true) wxOVERRIDE;
-
-    void update_list(std::vector<SearchOptions::Filter>& filters);
-
-private:
-    wxWindow*   panel;
-    wxListCtrl* search_ctrl{ nullptr };
-
-    void OnSize(wxSizeEvent& event);
-    void OnSetFocus(wxFocusEvent& event);
-    void OnKillFocus(wxFocusEvent& event);
-    void OnSelect(wxListEvent& event);
-};
-
 
 
 class SearchComboPopup : public wxListBox, public wxComboPopup
@@ -208,13 +142,9 @@ protected:
     wxString m_input_string;
 };
 
-class SearchCtrl
+class SearchCtrl : public wxComboCtrl
 {
-    wxBoxSizer*         box_sizer   {nullptr};
-    wxTextCtrl*         search_line {nullptr};
-    ScalableButton*     search_btn  {nullptr};
-    PopupSearchList*    popup_win   {nullptr};
-
+    SearchComboPopup*   popupListBox {nullptr};
 
     bool                prevent_update{ false };
     wxString            default_string;
@@ -223,28 +153,20 @@ class SearchCtrl
     void PopupList(wxCommandEvent& event);
     void OnInputText(wxCommandEvent& event);
 
-    wxComboCtrl*        comboCtrl {nullptr};
-    SearchComboPopup*   popupListBox {nullptr};
-
     void OnSelect(wxCommandEvent& event);
-    void OnLeftDownInPopup(wxEvent& event);
     void OnLeftUpInTextCtrl(wxEvent& event);
 
 public:
     SearchCtrl(wxWindow* parent);
-    ~SearchCtrl();
-
-    wxBoxSizer* sizer() const { return box_sizer; }
+    ~SearchCtrl() {}
 
     void		set_search_line(const std::string& search_line);
     void        msw_rescale();
-    void        select(int selection);
 
     void        update_list(std::vector<SearchOptions::Filter>& filters);
 };
 
 
-
 }}
 
 #endif //slic3r_SearchComboBox_hpp_
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 333840986..59f7791c0 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -39,6 +39,28 @@ namespace GUI {
 wxDEFINE_EVENT(EVT_TAB_VALUE_CHANGED, wxCommandEvent);
 wxDEFINE_EVENT(EVT_TAB_PRESETS_CHANGED, SimpleEvent);
 
+void Tab::Highlighter::init(Field* f)
+{
+    field = f;
+    field->activate_attention_bmp();
+}
+
+void Tab::Highlighter::invalidate()
+{
+    field->invalidate_attention_bmp();
+    field = nullptr;
+    blink_counter = 0;
+}
+
+bool Tab::Highlighter::blink()
+{
+    field->blink_attention_bmp();
+    if ((++blink_counter) == 29)
+        invalidate();
+
+    return blink_counter != 0;
+}
+
 Tab::Tab(wxNotebook* parent, const wxString& title, Preset::Type type) :
     m_parent(parent), m_title(title), m_type(type)
 {
@@ -123,7 +145,6 @@ void Tab::create_preset_tab()
     m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1));
 
     // search combox
-//    m_search_cb = new SearchComboBox(panel, wxGetApp().sidebar().get_search_list());
     m_search = new SearchCtrl(panel);
 
     auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
@@ -206,8 +227,7 @@ void Tab::create_preset_tab()
     m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
-//    m_hsizer->Add(m_search_cb, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->Add(m_search->sizer(), 0, wxALIGN_CENTER_VERTICAL);
+    m_hsizer->Add(m_search, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->AddSpacer(int(16 * scale_factor));
 //    m_hsizer->AddSpacer(int(32 * scale_factor));
 //    m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
@@ -1001,7 +1021,11 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
     // focused selected field
     if (field) {
         field->getWindow()->SetFocus();
-        m_highlighting_timer.Start(500, false);
+        if (m_highlighting_timer.IsRunning()) {
+            m_highlighting_timer.Stop();
+            m_highlighter.invalidate();
+        }
+        m_highlighting_timer.Start(100, false);
         m_highlighter.init(field);
     }
 }
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 4a6ab7c19..e16b44819 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -225,31 +225,14 @@ protected:
     ConfigOptionMode    m_mode = comExpert; // to correct first Tab update_visibility() set mode to Expert
 
 	wxTimer             m_highlighting_timer;
-	struct {
+	struct Highlighter
+	{
+		void init(Field* f);
+		void invalidate();
+		bool blink();
+	private:
 	    Field*	field {nullptr};
 		int		blink_counter {0};
-
-		void init(Field* f)
-		{
-		    field = f;
-			field->activate_attention_bmp();
-		}
-
-		void invalidate()
-		{
-			field->invalidate_attention_bmp();
-		    field = nullptr;
-			blink_counter = 0;
-		}
-
-		bool blink()
-		{
-			field->blink_attention_bmp();
-			if ((++blink_counter) == 5)
-			    invalidate();
-
-			return blink_counter != 0;
-		}
 	} m_highlighter;
 
 public:

From 8ab7956c26c95c2d90b7f651fda50d7c0d65fdcb Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 8 Apr 2020 09:47:33 +0200
Subject: [PATCH 21/55] Search: Added shortkey Ctrl+F on Plater

---
 src/slic3r/GUI/GLCanvas3D.cpp | 23 ++++++++++++++++++++++-
 src/slic3r/GUI/GLCanvas3D.hpp |  1 +
 src/slic3r/GUI/Search.cpp     |  5 ++++-
 3 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 0f9d49281..22f5670c0 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3032,6 +3032,16 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
         break;
 
 
+#ifdef __APPLE__
+        case 'f':
+        case 'F':
+#else /* __APPLE__ */
+        case WXK_CONTROL_F:
+#endif /* __APPLE__ */
+            _activate_search_toolbar_item();
+            break;
+
+
 #ifdef __APPLE__
         case 'y':
         case 'Y':
@@ -5029,7 +5039,7 @@ bool GLCanvas3D::_init_main_toolbar()
 
     item.name = "search";
     item.icon_filename = "search_.svg";
-    item.tooltip = _utf8(L("Search"));
+    item.tooltip = _utf8(L("Search")) + " [" + GUI::shortkey_ctrl_prefix() + "F]";
     item.sprite_id = 11;
     item.left.render_callback = [this](float left, float right, float, float) {
         if (m_canvas != nullptr)
@@ -7237,6 +7247,17 @@ bool GLCanvas3D::_deactivate_search_toolbar_item()
     return false;
 }
 
+bool GLCanvas3D::_activate_search_toolbar_item()
+{
+    if (!m_main_toolbar.is_item_pressed("search"))
+    {
+        m_main_toolbar.force_left_action(m_main_toolbar.get_item_id("search"), *this);
+        return true;
+    }
+
+    return false;
+}
+
 const Print* GLCanvas3D::fff_print() const
 {
     return (m_process == nullptr) ? nullptr : m_process->fff_print();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 58fd628d4..60f62636d 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -838,6 +838,7 @@ private:
 
     bool _deactivate_undo_redo_toolbar_items();
     bool _deactivate_search_toolbar_item();
+    bool _activate_search_toolbar_item();
 
     static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
 
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index e1ca8865e..26ed60b21 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -219,7 +219,7 @@ const SearchOptions::Option& SearchOptions::get_option(size_t pos_in_filter) con
 //------------------------------------------
 
 SearchCtrl::SearchCtrl(wxWindow* parent) :
-    wxComboCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER)
+    wxComboCtrl(parent, wxID_ANY, _L("Type here to search"), wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER)
 {
     default_string = _L("Type here to search");
 
@@ -263,6 +263,9 @@ void SearchCtrl::OnInputText(wxCommandEvent& )
 void SearchCtrl::PopupList(wxCommandEvent& e)
 {
     update_list(wxGetApp().sidebar().get_search_list().filters);
+    if (e.GetEventType() == wxEVT_TEXT_ENTER)
+        this->Popup();
+
     e.Skip();
 }
 

From 218abacb759275d5f005678c988d030df00a4b83 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 8 Apr 2020 20:22:38 +0200
Subject: [PATCH 22/55] Search: Set focus on search line in popup control on
 Plater

---
 src/slic3r/GUI/GLCanvas3D.cpp   |  6 ++++--
 src/slic3r/GUI/ImGuiWrapper.cpp | 13 +++++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 22f5670c0..67b135da5 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4489,7 +4489,7 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
     char *s = new char[255];
     strcpy(s, search_line.empty() ? _u8L("Type here to search").c_str() : search_line.c_str());
 
-    imgui->search_list(ImVec2(22 * em, 30 * em), &search_string_getter, s, selected, edited);
+    imgui->search_list(ImVec2(36 * em, 30 * em), &search_string_getter, s, selected, edited);
 
     search_line = s;
     delete [] s;
@@ -4499,7 +4499,9 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
 
     if (selected != size_t(-1))
     {
-        wxGetApp().sidebar().jump_to_option(selected);
+        // selected == 9999 means that Esc kye was pressed
+        if (selected != 9999)
+            wxGetApp().sidebar().jump_to_option(selected);
         action_taken = true;
     }
 
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 06a54b11c..2faf14952 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -570,9 +570,22 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
         const ImGuiID id = ImGui::GetID(search_str);
         ImVec2 search_size = ImVec2(size.x, ImGui::GetTextLineHeightWithSpacing() + style.ItemSpacing.y);
 
+        if (!ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))
+            ImGui::SetKeyboardFocusHere(0);
+
+        // The press on Esc key invokes editing of InputText (removes last changes)
+        // So we should save previous value...
+        std::string str = search_str;
         ImGui::InputTextEx("", NULL, search_str, 20, search_size, 0, NULL, NULL);
         edited = ImGui::IsItemEdited();
 
+        if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape))) {
+            // use 9999 to mark selection as a Esc key
+            selected = 9999;
+            // ... and when Esc key was pressed, than revert search_str value
+            strcpy(search_str, str.c_str());
+        }
+
         ImGui::BeginChildFrame(id, frame_bb.GetSize());
     }
 

From 167f7cf5de438aef0be5fedccd81e38b52e31743 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 13 Apr 2020 17:55:38 +0200
Subject: [PATCH 23/55] Added map for save Group and Category values for each
 option

+ Some code refactoring in Tab (the translation of the titles moved to the OptionGroups) and Search
+ Fixed assert in fts_fuzzy_match
---
 src/libslic3r/PrintConfig.cpp    |   8 +-
 src/slic3r/GUI/GLCanvas3D.cpp    |   4 +-
 src/slic3r/GUI/OptionsGroup.cpp  |   3 +
 src/slic3r/GUI/OptionsGroup.hpp  |   9 +-
 src/slic3r/GUI/Plater.cpp        |  32 ++-
 src/slic3r/GUI/Plater.hpp        |   6 +-
 src/slic3r/GUI/Search.cpp        | 143 ++++++++------
 src/slic3r/GUI/Search.hpp        | 123 +++++++-----
 src/slic3r/GUI/Tab.cpp           | 330 ++++++++++++++++---------------
 src/slic3r/GUI/Tab.hpp           |   5 +-
 src/slic3r/GUI/fts_fuzzy_match.h |   8 +-
 11 files changed, 373 insertions(+), 298 deletions(-)

diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index c7a7a9c8e..d62bc6d81 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -144,6 +144,7 @@ void PrintConfigDef::init_fff_params()
     def->label = L("Other layers");
     def->tooltip = L("Bed temperature for layers after the first one. "
                    "Set this to zero to disable bed temperature control commands in the output.");
+    def->sidetext = L("°C");
     def->full_label = L("Bed temperature");
     def->min = 0;
     def->max = 300;
@@ -866,8 +867,10 @@ void PrintConfigDef::init_fff_params()
 
     def = this->add("first_layer_bed_temperature", coInts);
     def->label = L("First layer");
+    def->full_label = L("First layer bed temperature");
     def->tooltip = L("Heated build plate temperature for the first layer. Set this to zero to disable "
                    "bed temperature control commands in the output.");
+    def->sidetext = L("°C");
     def->max = 0;
     def->max = 300;
     def->set_default_value(new ConfigOptionInts { 0 });
@@ -908,8 +911,10 @@ void PrintConfigDef::init_fff_params()
 
     def = this->add("first_layer_temperature", coInts);
     def->label = L("First layer");
+    def->full_label = L("First layer extruder temperature");
     def->tooltip = L("Extruder temperature for first layer. If you want to control temperature manually "
                    "during print, set this to zero to disable temperature control commands in the output file.");
+    def->sidetext = L("°C");
     def->min = 0;
     def->max = max_temp;
     def->set_default_value(new ConfigOptionInts { 200 });
@@ -2071,7 +2076,8 @@ void PrintConfigDef::init_fff_params()
     def->label = L("Other layers");
     def->tooltip = L("Extruder temperature for layers after the first one. Set this to zero to disable "
                    "temperature control commands in the output.");
-    def->full_label = L("Temperature");
+    def->sidetext = L("°C");
+    def->full_label = L("Extruder temperature");
     def->min = 0;
     def->max = max_temp;
     def->set_default_value(new ConfigOptionInts { 200 });
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 67b135da5..9fa1d8066 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4489,13 +4489,13 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
     char *s = new char[255];
     strcpy(s, search_line.empty() ? _u8L("Type here to search").c_str() : search_line.c_str());
 
-    imgui->search_list(ImVec2(36 * em, 30 * em), &search_string_getter, s, selected, edited);
+    imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, selected, edited);
 
     search_line = s;
     delete [] s;
 
     if (edited)
-        wxGetApp().sidebar().apply_search_filter();
+        wxGetApp().sidebar().search_and_apply_tab_search_lines();
 
     if (selected != size_t(-1))
     {
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index ca26dfe80..bafcac3a4 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -379,6 +379,9 @@ Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index
 	std::pair<std::string, int> pair(opt_key, opt_index);
 	m_opt_map.emplace(opt_id, pair);
 
+	if (m_show_modified_btns) // fill group and category values just fro options from Settings Tab 
+	    wxGetApp().sidebar().get_searcher().add_key(opt_id, title, config_category);
+
 	return Option(*m_config->def()->get(opt_key), opt_id);
 }
 
diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp
index 6c7c8c10d..6128eb7ab 100644
--- a/src/slic3r/GUI/OptionsGroup.hpp
+++ b/src/slic3r/GUI/OptionsGroup.hpp
@@ -12,6 +12,7 @@
 
 #include "Field.hpp"
 #include "GUI_App.hpp"
+#include "I18N.hpp"
 
 // Translate the ifdef 
 #ifdef __WXOSX__
@@ -59,7 +60,7 @@ public:
 		m_extra_widgets.push_back(widget);
     }
 	Line(wxString label, wxString tooltip) :
-		label(label), label_tooltip(tooltip) {}
+		label(_(label)), label_tooltip(_(tooltip)) {}
 
     const std::vector<widget_t>&	get_extra_widgets() const {return m_extra_widgets;}
     const std::vector<Option>&		get_options() const { return m_options; }
@@ -78,7 +79,7 @@ class OptionsGroup {
 	wxStaticBox*	stb;
 public:
     const bool		staticbox {true};
-    const wxString	title {wxString("")};
+    const wxString	title;
     size_t			label_width = 20 ;// {200};
     wxSizer*		sizer {nullptr};
     column_t		extra_column {nullptr};
@@ -175,7 +176,7 @@ public:
                     m_show_modified_btns(is_tab_opt),
 					staticbox(title!=""), extra_column(extra_clmn) {
         if (staticbox) {
-            stb = new wxStaticBox(_parent, wxID_ANY, title);
+            stb = new wxStaticBox(_parent, wxID_ANY, _(title));
             if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
             stb->SetFont(wxGetApp().bold_font());
         } else
@@ -248,6 +249,8 @@ public:
     bool					m_full_labels {0};
 	t_opt_map				m_opt_map;
 
+    std::string             config_category;
+
     void        set_config(DynamicPrintConfig* config) { m_config = config; }
 	Option		get_option(const std::string& opt_key, int opt_index = -1);
 	Line		create_single_option_line(const std::string& title, int idx = -1) /*const*/{
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 3d2f6052f..0dbd11827 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -722,8 +722,8 @@ struct Sidebar::priv
 	ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
 
     bool                is_collapsed {false};
-    SearchOptions		search_list;
-    std::string         search_line;
+    Search::OptionsSearcher     searcher;
+    std::string                 search_line;
 
     priv(Plater *plater) : plater(plater) {}
     ~priv();
@@ -1091,15 +1091,15 @@ void Sidebar::msw_rescale()
     p->scrolled->Layout();
 }
 
-void Sidebar::apply_search_filter()
+void Sidebar::search_and_apply_tab_search_lines()
 {
-    if (p->search_list.apply_filters(p->search_line))
+    if (p->searcher.search(p->search_line))
         apply_search_line_on_tabs();
 }
 
 void Sidebar::jump_to_option(size_t selected)
 {
-    const SearchOptions::Option& opt = p->search_list.get_option(selected);
+    const Search::Option& opt = p->searcher.get_option(selected);
     wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key, opt.category);
 }
 
@@ -1360,15 +1360,15 @@ bool Sidebar::is_multifilament()
     return p->combos_filament.size() > 1;
 }
 
-static std::vector<SearchInput> get_search_inputs(ConfigOptionMode mode)
+static std::vector<Search::InputInfo> get_search_inputs(ConfigOptionMode mode)
 {
-    std::vector<SearchInput> ret {};
+    std::vector<Search::InputInfo> ret {};
 
     auto& tabs_list = wxGetApp().tabs_list;
     auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
     for (auto tab : tabs_list)
         if (tab->supports_printer_technology(print_tech))
-            ret.emplace_back(SearchInput{tab->get_config(), tab->type(), mode});
+            ret.emplace_back(Search::InputInfo {tab->get_config(), tab->type(), mode});
 
     return ret;
 }
@@ -1380,14 +1380,12 @@ void Sidebar::apply_search_line_on_tabs()
 
     for (auto tab : tabs_list)
         if (tab->supports_printer_technology(print_tech))
-            //tab->get_search_cb()->update_combobox();
             tab->set_search_line(p->search_line);
 }
 
-void Sidebar::update_search_list()
+void Sidebar::update_searcher()
 {
-    p->search_list.init(get_search_inputs(m_mode));
-//    apply_search_line_on_tabs();
+    p->searcher.init(get_search_inputs(m_mode));
 }
 
 void Sidebar::update_mode()
@@ -1396,7 +1394,7 @@ void Sidebar::update_mode()
 
     update_reslice_btn_tooltip();
     update_mode_sizer();
-    update_search_list();
+    update_searcher();
 
     wxWindowUpdateLocker noUpdates(this);
 
@@ -1428,9 +1426,9 @@ std::vector<PresetComboBox*>& Sidebar::combos_filament()
     return p->combos_filament;
 }
 
-SearchOptions& Sidebar::get_search_list()
+Search::OptionsSearcher& Sidebar::get_searcher()
 {
-    return p->search_list;
+    return p->searcher;
 }
 
 std::string& Sidebar::get_search_line()
@@ -3682,7 +3680,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
         wxGetApp().obj_list()->update_object_list_by_printer_technology();
 
         // print technology could be changed, so we should to update a search list
-        sidebar->update_search_list();
+        sidebar->update_searcher();
     }
 }
 
@@ -5347,7 +5345,7 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou
 
 bool Plater::search_string_getter(int idx, const char** out_text)
 {
-    const SearchOptions& search_list = p->sidebar->get_search_list();
+    const Search::OptionsSearcher& search_list = p->sidebar->get_searcher();
     
     if (0 <= idx && (size_t)idx < search_list.size()) {
         search_list[idx].get_marked_label(out_text);
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 6544d4554..52a24ef36 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -105,7 +105,7 @@ public:
     void update_mode_sizer() const;
     void update_reslice_btn_tooltip() const;
     void msw_rescale();
-    void apply_search_filter();
+    void search_and_apply_tab_search_lines();
     void jump_to_option(size_t selected);
 
     ObjectManipulation*     obj_manipul();
@@ -133,10 +133,10 @@ public:
     bool                    is_collapsed();
     void                    collapse(bool collapse);
     void                    apply_search_line_on_tabs();
-    void                    update_search_list();
+    void                    update_searcher();
 
     std::vector<PresetComboBox*>&   combos_filament();
-    SearchOptions&                  get_search_list();
+    Search::OptionsSearcher&        get_searcher();
     std::string&                    get_search_line();
 
 private:
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 26ed60b21..49f74b340 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -1,19 +1,9 @@
 #include "Search.hpp"
 
 #include <cstddef>
-#include <algorithm>
-#include <numeric>
-#include <vector>
 #include <string>
-#include <regex>
-#include <future>
-#include <boost/algorithm/string.hpp>
 #include <boost/optional.hpp>
-#include <boost/filesystem/path.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/log/trivial.hpp>
 
-//#include <wx/bmpcbox.h>
 #include "libslic3r/PrintConfig.hpp"
 #include "GUI_App.hpp"
 #include "Tab.hpp"
@@ -27,56 +17,76 @@
 using boost::optional;
 
 namespace Slic3r {
-namespace GUI {
 
-bool SearchOptions::Option::fuzzy_match_simple(char const * search_pattern) const
+using GUI::from_u8;
+using GUI::into_u8;
+
+namespace Search {
+
+FMFlag Option::fuzzy_match_simple(char const * search_pattern) const
 {
-    char const* opt_key_str    = opt_key.c_str();
-    char const* label_str      = label.utf8_str();
-
-    return  fts::fuzzy_match_simple(search_pattern, label_str   )   ||
-            fts::fuzzy_match_simple(search_pattern, opt_key_str )   ; 
+    return  fts::fuzzy_match_simple(search_pattern, label_local.utf8_str())     ? fmLabelLocal      :
+            fts::fuzzy_match_simple(search_pattern, group_local.utf8_str())     ? fmGroupLocal      :
+            fts::fuzzy_match_simple(search_pattern, category_local.utf8_str())  ? fmCategoryLocal   :
+            fts::fuzzy_match_simple(search_pattern, opt_key.c_str())            ? fmOptKey          :
+            fts::fuzzy_match_simple(search_pattern, label.utf8_str())           ? fmLabel           :
+            fts::fuzzy_match_simple(search_pattern, group.utf8_str())           ? fmGroup           :
+            fts::fuzzy_match_simple(search_pattern, category.utf8_str())        ? fmCategory        : fmUndef   ;
 }
 
-bool SearchOptions::Option::fuzzy_match_simple(const wxString& search) const
+FMFlag Option::fuzzy_match_simple(const wxString& search) const
 {
     char const* search_pattern = search.utf8_str();
     return fuzzy_match_simple(search_pattern);
 }
 
-bool SearchOptions::Option::fuzzy_match_simple(const std::string& search) const
+FMFlag Option::fuzzy_match_simple(const std::string& search) const
 {
     char const* search_pattern = search.c_str();
     return fuzzy_match_simple(search_pattern);
 }
 
-bool SearchOptions::Option::fuzzy_match(char const* search_pattern, int& outScore)
+FMFlag Option::fuzzy_match(char const* search_pattern, int& outScore) const
 {
-    char const* opt_key_str    = opt_key.c_str();
-    char const* label_str      = label.utf8_str();
+    FMFlag flag = fmUndef;
+    int score;
 
-    return (fts::fuzzy_match(search_pattern, label_str   , outScore)   ||
-            fts::fuzzy_match(search_pattern, opt_key_str , outScore)   ); 
+    if (fts::fuzzy_match(search_pattern, label_local.utf8_str(),    score) && outScore < score) {
+        outScore = score; flag = fmLabelLocal   ; }
+    if (fts::fuzzy_match(search_pattern, group_local.utf8_str(),    score) && outScore < score) {
+        outScore = score; flag = fmGroupLocal   ; }
+    if (fts::fuzzy_match(search_pattern, category_local.utf8_str(), score) && outScore < score) {
+        outScore = score; flag = fmCategoryLocal; }
+    if (fts::fuzzy_match(search_pattern, opt_key.c_str(),           score) && outScore < score) {
+        outScore = score; flag = fmOptKey       ; }
+    if (fts::fuzzy_match(search_pattern, label.utf8_str(),          score) && outScore < score) {
+        outScore = score; flag = fmLabel        ; }
+    if (fts::fuzzy_match(search_pattern, group.utf8_str(),          score) && outScore < score) {
+        outScore = score; flag = fmGroup        ; }
+    if (fts::fuzzy_match(search_pattern, category.utf8_str(),       score) && outScore < score) {
+        outScore = score; flag = fmCategory     ; }
+
+    return flag;
 }
 
-bool SearchOptions::Option::fuzzy_match(const wxString& search, int& outScore)
+FMFlag Option::fuzzy_match(const wxString& search, int& outScore) const
 {
     char const* search_pattern = search.utf8_str();
     return fuzzy_match(search_pattern, outScore); 
 }
 
-bool SearchOptions::Option::fuzzy_match(const std::string& search, int& outScore)
+FMFlag Option::fuzzy_match(const std::string& search, int& outScore) const
 {
     char const* search_pattern = search.c_str();
     return fuzzy_match(search_pattern, outScore);
 }
 
-void SearchOptions::Filter::get_label(const char** out_text) const
+void FoundOption::get_label(const char** out_text) const
 {
     *out_text = label.utf8_str();
 }
 
-void SearchOptions::Filter::get_marked_label(const char** out_text) const
+void FoundOption::get_marked_label(const char** out_text) const
 {
     *out_text = marked_label.utf8_str();
 }
@@ -89,8 +99,10 @@ void change_opt_key(std::string& opt_key, DynamicPrintConfig* config)
         opt_key += "#" + std::to_string(0);
 }
 
-void SearchOptions::append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
+void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
 {
+    std::vector<std::string> non_added_options {"printer_technology", "thumbnails" };
+
     for (std::string opt_key : config->keys())
     {
         const ConfigOptionDef& opt = config->def()->options.at(opt_key);
@@ -109,13 +121,15 @@ void SearchOptions::append_options(DynamicPrintConfig* config, Preset::Type type
             default:		break;
             }
 
-        wxString label;
-        if (!opt.category.empty())
-            label += _(opt.category) + " : ";
-        label += _(opt.full_label.empty() ? opt.label : opt.full_label);
+        wxString label = opt.full_label.empty() ? opt.label : opt.full_label;
+
+        const GroupAndCategory& gc = groups_and_categories[opt_key];
 
         if (!label.IsEmpty())
-            options.emplace_back(Option{ label, opt_key, opt.category, type });
+            options.emplace_back(Option{opt_key, type,
+                                        label, _(label),
+                                        gc.group, _(gc.group),
+                                        gc.category, _(gc.category) });
     }
 }
 
@@ -163,54 +177,69 @@ static void clear_marked_string(wxString& str)
     str.Replace(delete_string, wxEmptyString, true);
 }
 
-bool SearchOptions::apply_filters(const std::string& search, bool force/* = false*/)
+bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
 {
     if (search_line == search && !force)
         return false;
 
-    clear_filters();
+    found.clear();
 
     bool full_list = search.empty();
 
     for (size_t i=0; i < options.size(); i++)
     {
+        const Option &opt = options[i];
         if (full_list) {
-            filters.emplace_back(Filter{ options[i].label, options[i].label, i, 0 });
+            wxString label = opt.category_local + " > " + opt.group_local + " > " + opt.label_local;
+            found.emplace_back(FoundOption{ label, label, i, 0 });
             continue;
         }
 
         int score = 0;
-        if (options[i].fuzzy_match_simple(search)/*fuzzy_match(search, score)*/)
+
+        FMFlag fuzzy_match_flag = opt.fuzzy_match(search, score);
+        if (fuzzy_match_flag != fmUndef)
         {
-            wxString marked_label = options[i].label;
+            wxString label = opt.category_local + " > " + opt.group_local  + " > " + opt.label_local;
+            if (     fuzzy_match_flag == fmLabel   ) label += "(" + opt.label    + ")";
+            else if (fuzzy_match_flag == fmGroup   ) label += "(" + opt.group    + ")";
+            else if (fuzzy_match_flag == fmCategory) label += "(" + opt.category + ")";
+            else if (fuzzy_match_flag == fmOptKey  ) label += "(" + opt.opt_key  + ")";
+
+            wxString marked_label = label;
             mark_string(marked_label, from_u8(search));
             clear_marked_string(marked_label);
 
-            filters.emplace_back(Filter{ options[i].label, marked_label, i, score });
+            found.emplace_back(FoundOption{ label, marked_label, i, score });
         }
     }
 
     if (!full_list)
-        sort_filters();
+        sort_found();
 
     search_line = search;
     return true;
 }
 
-void SearchOptions::init(std::vector<SearchInput> input_values)
+void OptionsSearcher::init(std::vector<InputInfo> input_values)
 {
-    clear_options();
+    options.clear();
     for (auto i : input_values)
         append_options(i.config, i.type, i.mode);
     sort_options();
 
-    apply_filters(search_line, true);
+    search(search_line, true);
 }
 
-const SearchOptions::Option& SearchOptions::get_option(size_t pos_in_filter) const
+const Option& OptionsSearcher::get_option(size_t pos_in_filter) const
 {
-    assert(pos_in_filter != size_t(-1) && filters[pos_in_filter].option_idx != size_t(-1));
-    return options[filters[pos_in_filter].option_idx];
+    assert(pos_in_filter != size_t(-1) && found[pos_in_filter].option_idx != size_t(-1));
+    return options[found[pos_in_filter].option_idx];
+}
+
+void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group, const wxString& category)
+{
+    groups_and_categories[opt_key] = GroupAndCategory{group, category};
 }
 
 
@@ -219,7 +248,7 @@ const SearchOptions::Option& SearchOptions::get_option(size_t pos_in_filter) con
 //------------------------------------------
 
 SearchCtrl::SearchCtrl(wxWindow* parent) :
-    wxComboCtrl(parent, wxID_ANY, _L("Type here to search"), wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER)
+    wxComboCtrl(parent, wxID_ANY, _L("Type here to search"), wxDefaultPosition, wxSize(25 * GUI::wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER)
 {
     default_string = _L("Type here to search");
 
@@ -253,16 +282,16 @@ void SearchCtrl::OnInputText(wxCommandEvent& )
     if (input_string == default_string)
         input_string.Clear();
 
-    wxGetApp().sidebar().get_search_line() = into_u8(input_string);
+    GUI::wxGetApp().sidebar().get_search_line() = into_u8(input_string);
 
     editing = true;
-    wxGetApp().sidebar().apply_search_filter();
+    GUI::wxGetApp().sidebar().search_and_apply_tab_search_lines();
     editing = false;
 }
 
 void SearchCtrl::PopupList(wxCommandEvent& e)
 {
-    update_list(wxGetApp().sidebar().get_search_list().filters);
+    update_list(GUI::wxGetApp().sidebar().get_searcher().found_options());
     if (e.GetEventType() == wxEVT_TEXT_ENTER)
         this->Popup();
 
@@ -278,7 +307,7 @@ void SearchCtrl::set_search_line(const std::string& line)
 
 void SearchCtrl::msw_rescale()
 {
-    wxSize size = wxSize(25 * wxGetApp().em_unit(), -1);
+    wxSize size = wxSize(25 * GUI::wxGetApp().em_unit(), -1);
     // Set rescaled min height to correct layout
     this->SetMinSize(size);
 
@@ -294,11 +323,11 @@ void SearchCtrl::OnSelect(wxCommandEvent& event)
         return;
 
     prevent_update = true;
-    wxGetApp().sidebar().jump_to_option(selection);
+    GUI::wxGetApp().sidebar().jump_to_option(selection);
     prevent_update = false;
 }
 
-void SearchCtrl::update_list(std::vector<SearchOptions::Filter>& filters)
+void SearchCtrl::update_list(const std::vector<FoundOption>& filters)
 {
     if (popupListBox->GetCount() == filters.size() &&
         popupListBox->GetString(0) == filters[0].label &&
@@ -306,7 +335,7 @@ void SearchCtrl::update_list(std::vector<SearchOptions::Filter>& filters)
         return;
 
     popupListBox->Clear();
-    for (const SearchOptions::Filter& item : filters)
+    for (const FoundOption& item : filters)
         popupListBox->Append(item.label);
 }
 
@@ -318,4 +347,6 @@ void SearchCtrl::OnLeftUpInTextCtrl(wxEvent &event)
     event.Skip();
 }
 
-}}    // namespace Slic3r::GUI
+}
+
+}    // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index f083166f5..8488781a3 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -2,11 +2,10 @@
 #define slic3r_SearchComboBox_hpp_
 
 #include <vector>
+#include <map>
 
 #include <wx/panel.h>
 #include <wx/sizer.h>
-//#include <wx/bmpcbox.h>
-#include <wx/popupwin.h>
 #include <wx/listctrl.h>
 
 #include <wx/combo.h>
@@ -17,72 +16,98 @@
 
 namespace Slic3r {
 
-namespace GUI {
+namespace Search{
 
-struct SearchInput
+struct InputInfo
 {
     DynamicPrintConfig* config  {nullptr};
     Preset::Type        type    {Preset::TYPE_INVALID};
     ConfigOptionMode    mode    {comSimple};
 };
 
-class SearchOptions
+struct GroupAndCategory {
+    wxString        group;
+    wxString        category;
+};
+
+// fuzzy_match flag
+enum FMFlag
+{
+    fmUndef = 0, // didn't find 
+    fmOptKey,
+    fmLabel,
+    fmLabelLocal,
+    fmGroup,
+    fmGroupLocal,
+    fmCategory,
+    fmCategoryLocal
+};
+
+struct Option {
+    bool operator<(const Option& other) const { return other.label > this->label; }
+    bool operator>(const Option& other) const { return other.label < this->label; }
+
+    std::string     opt_key;
+    Preset::Type    type {Preset::TYPE_INVALID};
+    wxString        label;
+    wxString        label_local;
+    wxString        group;
+    wxString        group_local;
+    wxString        category;
+    wxString        category_local;
+
+    FMFlag fuzzy_match_simple(char const *search_pattern) const;
+    FMFlag fuzzy_match_simple(const wxString& search) const;
+    FMFlag fuzzy_match_simple(const std::string &search) const;
+    FMFlag fuzzy_match(char const *search_pattern, int &outScore) const;
+    FMFlag fuzzy_match(const wxString &search, int &outScore) const ;
+    FMFlag fuzzy_match(const std::string &search, int &outScore) const ;
+};
+
+struct FoundOption {
+    wxString        label;
+    wxString        marked_label;
+    size_t          option_idx {0};
+    int             outScore {0};
+
+    void get_label(const char** out_text) const;
+    void get_marked_label(const char** out_text) const;
+};
+
+class OptionsSearcher
 {
     std::string         search_line;
-public:
-    struct Option {
-        bool operator<(const Option& other) const { return other.label > this->label; }
-        bool operator>(const Option& other) const { return other.label < this->label; }
+    std::map<std::string, GroupAndCategory> groups_and_categories;
 
-        wxString        label;
-        std::string     opt_key;
-        wxString        category;
-        Preset::Type    type {Preset::TYPE_INVALID};
-        // wxString     grope;
-
-        bool fuzzy_match_simple(char const *search_pattern) const;
-        bool fuzzy_match_simple(const wxString& search) const;
-        bool fuzzy_match_simple(const std::string &search) const;
-        bool fuzzy_match(char const *search_pattern, int &outScore);
-        bool fuzzy_match(const wxString &search, int &outScore);
-        bool fuzzy_match(const std::string &search, int &outScore);
-    };
-    std::vector<Option> options {};
-
-    struct Filter {
-        wxString        label;
-        wxString        marked_label;
-        size_t          option_idx {0};
-        int             outScore {0};
-
-        void get_label(const char** out_text) const;
-        void get_marked_label(const char** out_text) const;
-    };
-    std::vector<Filter> filters {};
-
-    void clear_options() { options.clear(); }
-    void clear_filters() { filters.clear(); }
-
-    void init(std::vector<SearchInput> input_values);
+    std::vector<Option>         options {};
+    std::vector<FoundOption>    found {};
 
     void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
-    bool apply_filters(const std::string& search, bool force = false);
 
     void sort_options() {
         std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) {
             return o1.label < o2.label; });
     }
-    void sort_filters() {
-        std::sort(filters.begin(), filters.end(), [](const Filter& f1, const Filter& f2) {
+    void sort_found() {
+        std::sort(found.begin(), found.end(), [](const FoundOption& f1, const FoundOption& f2) {
             return f1.outScore > f2.outScore; });
     };
 
     size_t options_size() const { return options.size(); }
-    size_t filters_size() const { return filters.size(); }
-    size_t size() const         { return filters_size(); }
+    size_t found_size() const { return found.size(); }
 
-    const Filter& operator[](const size_t pos) const noexcept { return filters[pos]; }
+public:
+    void init(std::vector<InputInfo> input_values);
+    bool search(const std::string& search, bool force = false);
+
+    void add_key(const std::string& opt_key, const wxString& group, const wxString& category);
+
+    size_t size() const         { return found_size(); }
+
+    const FoundOption& operator[](const size_t pos) const noexcept { return found[pos]; }
     const Option& get_option(size_t pos_in_filter) const;
+
+    const std::vector<FoundOption>& found_options() { return found; }
 };
 
 
@@ -146,7 +171,7 @@ class SearchCtrl : public wxComboCtrl
 {
     SearchComboPopup*   popupListBox {nullptr};
 
-    bool                prevent_update{ false };
+    bool                prevent_update { false };
     wxString            default_string;
     bool                editing {false};
 
@@ -163,10 +188,10 @@ public:
     void		set_search_line(const std::string& search_line);
     void        msw_rescale();
 
-    void        update_list(std::vector<SearchOptions::Filter>& filters);
+    void        update_list(const std::vector<FoundOption>& filters);
 };
 
-
-}}
+} // Search namespace
+}
 
 #endif //slic3r_SearchComboBox_hpp_
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 59f7791c0..e63f7dff7 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -145,7 +145,7 @@ void Tab::create_preset_tab()
     m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1));
 
     // search combox
-    m_search = new SearchCtrl(panel);
+    m_search = new Search::SearchCtrl(panel);
 
     auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
@@ -424,7 +424,7 @@ void Tab::update_labels_colour()
         auto title = m_treectrl->GetItemText(cur_item);
         for (auto page : m_pages)
         {
-            if (page->title() != title)
+            if (_(page->title()) != title)
                 continue;
 
             const wxColor *clr = !page->m_is_nonsys_values ? &m_sys_label_clr :
@@ -623,17 +623,17 @@ void Tab::update_changed_tree_ui()
         auto title = m_treectrl->GetItemText(cur_item);
         for (auto page : m_pages)
         {
-            if (page->title() != title)
+            if (_(page->title()) != title)
                 continue;
             bool sys_page = true;
             bool modified_page = false;
-            if (title == _("General")) {
+            if (page->title() == "General") {
                 std::initializer_list<const char*> optional_keys{ "extruders_count", "bed_shape" };
                 for (auto &opt_key : optional_keys) {
                     get_sys_and_mod_flags(opt_key, sys_page, modified_page);
                 }
             }
-            if (title == _("Dependencies")) {
+            if (page->title() == "Dependencies") {
                 if (m_type == Slic3r::Preset::TYPE_PRINTER) {
                     sys_page = m_presets->get_selected_preset_parent() != nullptr;
                     modified_page = false;
@@ -700,20 +700,20 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
 
     auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
     for (auto page : m_pages)
-        if (page->title() == selection)	{
+        if (_(page->title()) == selection)	{
             for (auto group : page->m_optgroups) {
-                if (group->title == _("Capabilities")) {
+                if (group->title == "Capabilities") {
                     if ((m_options_list["extruders_count"] & os) == 0)
                         to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count");
                 }
-                if (group->title == _("Size and coordinates")) {
+                if (group->title == "Size and coordinates") {
                     if ((m_options_list["bed_shape"] & os) == 0) {
                         to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape");
                         load_key_value("bed_shape", true/*some value*/, true);
                     }
 
                 }
-                if (group->title == _("Profile dependencies")) {
+                if (group->title == "Profile dependencies") {
                     // "compatible_printers" option doesn't exists in Printer Settimgs Tab
                     if (m_type != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) {
                         to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers");
@@ -1003,7 +1003,7 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
 
     while (cur_item) {
         auto title = m_treectrl->GetItemText(cur_item);
-        if (page_title != title) {
+        if (_(page_title) != title) {
             cur_item = m_treectrl->GetNextVisible(cur_item);
             continue;
         }
@@ -1028,6 +1028,12 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
         m_highlighting_timer.Start(100, false);
         m_highlighter.init(field);
     }
+    else
+    {
+        // "bed_shape", "bed_custom_texture", "bed_custom_model"
+
+
+    }
 }
 
 
@@ -1210,12 +1216,12 @@ void TabPrint::build()
     m_presets = &m_preset_bundle->prints;
     load_initial_data();
 
-    auto page = add_options_page(_(L("Layers and perimeters")), "layers");
-        auto optgroup = page->new_optgroup(_(L("Layer height")));
+    auto page = add_options_page(L("Layers and perimeters"), "layers");
+        auto optgroup = page->new_optgroup(L("Layer height"));
         optgroup->append_single_option_line("layer_height");
         optgroup->append_single_option_line("first_layer_height");
 
-        optgroup = page->new_optgroup(_(L("Vertical shells")));
+        optgroup = page->new_optgroup(L("Vertical shells"));
         optgroup->append_single_option_line("perimeters");
         optgroup->append_single_option_line("spiral_vase");
 
@@ -1226,12 +1232,12 @@ void TabPrint::build()
         };
         optgroup->append_line(line);
 
-        optgroup = page->new_optgroup(_(L("Horizontal shells")));
-        line = { _(L("Solid layers")), "" };
+        optgroup = page->new_optgroup(L("Horizontal shells"));
+        line = { L("Solid layers"), "" };
         line.append_option(optgroup->get_option("top_solid_layers"));
         line.append_option(optgroup->get_option("bottom_solid_layers"));
         optgroup->append_line(line);
-    	line = { _(L("Minimum shell thickness")), "" };
+    	line = { L("Minimum shell thickness"), "" };
         line.append_option(optgroup->get_option("top_solid_min_thickness"));
         line.append_option(optgroup->get_option("bottom_solid_min_thickness"));
         optgroup->append_line(line);
@@ -1242,29 +1248,29 @@ void TabPrint::build()
 	    };
 	    optgroup->append_line(line);
 
-        optgroup = page->new_optgroup(_(L("Quality (slower slicing)")));
+        optgroup = page->new_optgroup(L("Quality (slower slicing)"));
         optgroup->append_single_option_line("extra_perimeters");
         optgroup->append_single_option_line("ensure_vertical_shell_thickness");
         optgroup->append_single_option_line("avoid_crossing_perimeters");
         optgroup->append_single_option_line("thin_walls");
         optgroup->append_single_option_line("overhangs");
 
-        optgroup = page->new_optgroup(_(L("Advanced")));
+        optgroup = page->new_optgroup(L("Advanced"));
         optgroup->append_single_option_line("seam_position");
         optgroup->append_single_option_line("external_perimeters_first");
 
-    page = add_options_page(_(L("Infill")), "infill");
-        optgroup = page->new_optgroup(_(L("Infill")));
+    page = add_options_page(L("Infill"), "infill");
+        optgroup = page->new_optgroup(L("Infill"));
         optgroup->append_single_option_line("fill_density");
         optgroup->append_single_option_line("fill_pattern");
         optgroup->append_single_option_line("top_fill_pattern");
         optgroup->append_single_option_line("bottom_fill_pattern");
 
-        optgroup = page->new_optgroup(_(L("Reducing printing time")));
+        optgroup = page->new_optgroup(L("Reducing printing time"));
         optgroup->append_single_option_line("infill_every_layers");
         optgroup->append_single_option_line("infill_only_where_needed");
 
-        optgroup = page->new_optgroup(_(L("Advanced")));
+        optgroup = page->new_optgroup(L("Advanced"));
         optgroup->append_single_option_line("solid_infill_every_layers");
         optgroup->append_single_option_line("fill_angle");
         optgroup->append_single_option_line("solid_infill_below_area");
@@ -1272,29 +1278,29 @@ void TabPrint::build()
         optgroup->append_single_option_line("only_retract_when_crossing_perimeters");
         optgroup->append_single_option_line("infill_first");
 
-    page = add_options_page(_(L("Skirt and brim")), "skirt+brim");
-        optgroup = page->new_optgroup(_(L("Skirt")));
+    page = add_options_page(L("Skirt and brim"), "skirt+brim");
+        optgroup = page->new_optgroup(L("Skirt"));
         optgroup->append_single_option_line("skirts");
         optgroup->append_single_option_line("skirt_distance");
         optgroup->append_single_option_line("skirt_height");
         optgroup->append_single_option_line("draft_shield");
         optgroup->append_single_option_line("min_skirt_length");
 
-        optgroup = page->new_optgroup(_(L("Brim")));
+        optgroup = page->new_optgroup(L("Brim"));
         optgroup->append_single_option_line("brim_width");
 
-    page = add_options_page(_(L("Support material")), "support");
-        optgroup = page->new_optgroup(_(L("Support material")));
+    page = add_options_page(L("Support material"), "support");
+        optgroup = page->new_optgroup(L("Support material"));
         optgroup->append_single_option_line("support_material");
         optgroup->append_single_option_line("support_material_auto");
         optgroup->append_single_option_line("support_material_threshold");
         optgroup->append_single_option_line("support_material_enforce_layers");
 
-        optgroup = page->new_optgroup(_(L("Raft")));
+        optgroup = page->new_optgroup(L("Raft"));
         optgroup->append_single_option_line("raft_layers");
 //		# optgroup->append_single_option_line(get_option_("raft_contact_distance");
 
-        optgroup = page->new_optgroup(_(L("Options for support material and raft")));
+        optgroup = page->new_optgroup(L("Options for support material and raft"));
         optgroup->append_single_option_line("support_material_contact_distance");
         optgroup->append_single_option_line("support_material_pattern");
         optgroup->append_single_option_line("support_material_with_sheath");
@@ -1308,8 +1314,8 @@ void TabPrint::build()
         optgroup->append_single_option_line("dont_support_bridges");
         optgroup->append_single_option_line("support_material_synchronize_layers");
 
-    page = add_options_page(_(L("Speed")), "time");
-        optgroup = page->new_optgroup(_(L("Speed for print moves")));
+    page = add_options_page(L("Speed"), "time");
+        optgroup = page->new_optgroup(L("Speed for print moves"));
         optgroup->append_single_option_line("perimeter_speed");
         optgroup->append_single_option_line("small_perimeter_speed");
         optgroup->append_single_option_line("external_perimeter_speed");
@@ -1321,20 +1327,20 @@ void TabPrint::build()
         optgroup->append_single_option_line("bridge_speed");
         optgroup->append_single_option_line("gap_fill_speed");
 
-        optgroup = page->new_optgroup(_(L("Speed for non-print moves")));
+        optgroup = page->new_optgroup(L("Speed for non-print moves"));
         optgroup->append_single_option_line("travel_speed");
 
-        optgroup = page->new_optgroup(_(L("Modifiers")));
+        optgroup = page->new_optgroup(L("Modifiers"));
         optgroup->append_single_option_line("first_layer_speed");
 
-        optgroup = page->new_optgroup(_(L("Acceleration control (advanced)")));
+        optgroup = page->new_optgroup(L("Acceleration control (advanced)"));
         optgroup->append_single_option_line("perimeter_acceleration");
         optgroup->append_single_option_line("infill_acceleration");
         optgroup->append_single_option_line("bridge_acceleration");
         optgroup->append_single_option_line("first_layer_acceleration");
         optgroup->append_single_option_line("default_acceleration");
 
-        optgroup = page->new_optgroup(_(L("Autospeed (advanced)")));
+        optgroup = page->new_optgroup(L("Autospeed (advanced)"));
         optgroup->append_single_option_line("max_print_speed");
         optgroup->append_single_option_line("max_volumetric_speed");
 #ifdef HAS_PRESSURE_EQUALIZER
@@ -1342,19 +1348,19 @@ void TabPrint::build()
         optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative");
 #endif /* HAS_PRESSURE_EQUALIZER */
 
-    page = add_options_page(_(L("Multiple Extruders")), "funnel");
-        optgroup = page->new_optgroup(_(L("Extruders")));
+    page = add_options_page(L("Multiple Extruders"), "funnel");
+        optgroup = page->new_optgroup(L("Extruders"));
         optgroup->append_single_option_line("perimeter_extruder");
         optgroup->append_single_option_line("infill_extruder");
         optgroup->append_single_option_line("solid_infill_extruder");
         optgroup->append_single_option_line("support_material_extruder");
         optgroup->append_single_option_line("support_material_interface_extruder");
 
-        optgroup = page->new_optgroup(_(L("Ooze prevention")));
+        optgroup = page->new_optgroup(L("Ooze prevention"));
         optgroup->append_single_option_line("ooze_prevention");
         optgroup->append_single_option_line("standby_temperature_delta");
 
-        optgroup = page->new_optgroup(_(L("Wipe tower")));
+        optgroup = page->new_optgroup(L("Wipe tower"));
         optgroup->append_single_option_line("wipe_tower");
         optgroup->append_single_option_line("wipe_tower_x");
         optgroup->append_single_option_line("wipe_tower_y");
@@ -1364,11 +1370,11 @@ void TabPrint::build()
         optgroup->append_single_option_line("wipe_tower_no_sparse_layers");
         optgroup->append_single_option_line("single_extruder_multi_material_priming");
 
-        optgroup = page->new_optgroup(_(L("Advanced")));
+        optgroup = page->new_optgroup(L("Advanced"));
         optgroup->append_single_option_line("interface_shells");
 
-    page = add_options_page(_(L("Advanced")), "wrench");
-        optgroup = page->new_optgroup(_(L("Extrusion width")));
+    page = add_options_page(L("Advanced"), "wrench");
+        optgroup = page->new_optgroup(L("Extrusion width"));
         optgroup->append_single_option_line("extrusion_width");
         optgroup->append_single_option_line("first_layer_extrusion_width");
         optgroup->append_single_option_line("perimeter_extrusion_width");
@@ -1378,51 +1384,51 @@ void TabPrint::build()
         optgroup->append_single_option_line("top_infill_extrusion_width");
         optgroup->append_single_option_line("support_material_extrusion_width");
 
-        optgroup = page->new_optgroup(_(L("Overlap")));
+        optgroup = page->new_optgroup(L("Overlap"));
         optgroup->append_single_option_line("infill_overlap");
 
-        optgroup = page->new_optgroup(_(L("Flow")));
+        optgroup = page->new_optgroup(L("Flow"));
         optgroup->append_single_option_line("bridge_flow_ratio");
 
-        optgroup = page->new_optgroup(_(L("Slicing")));
+        optgroup = page->new_optgroup(L("Slicing"));
         optgroup->append_single_option_line("slice_closing_radius");
         optgroup->append_single_option_line("resolution");
         optgroup->append_single_option_line("xy_size_compensation");
         optgroup->append_single_option_line("elefant_foot_compensation");
 
-        optgroup = page->new_optgroup(_(L("Other")));
+        optgroup = page->new_optgroup(L("Other"));
         optgroup->append_single_option_line("clip_multipart_objects");
 
-    page = add_options_page(_(L("Output options")), "output+page_white");
-        optgroup = page->new_optgroup(_(L("Sequential printing")));
+    page = add_options_page(L("Output options"), "output+page_white");
+        optgroup = page->new_optgroup(L("Sequential printing"));
         optgroup->append_single_option_line("complete_objects");
-        line = { _(L("Extruder clearance (mm)")), "" };
+        line = { L("Extruder clearance (mm)"), "" };
         line.append_option(optgroup->get_option("extruder_clearance_radius"));
         line.append_option(optgroup->get_option("extruder_clearance_height"));
         optgroup->append_line(line);
 
-        optgroup = page->new_optgroup(_(L("Output file")));
+        optgroup = page->new_optgroup(L("Output file"));
         optgroup->append_single_option_line("gcode_comments");
         optgroup->append_single_option_line("gcode_label_objects");
         Option option = optgroup->get_option("output_filename_format");
         option.opt.full_width = true;
         optgroup->append_single_option_line(option);
 
-        optgroup = page->new_optgroup(_(L("Post-processing scripts")), 0);
+        optgroup = page->new_optgroup(L("Post-processing scripts"), 0);
         option = optgroup->get_option("post_process");
         option.opt.full_width = true;
         option.opt.height = 5;//50;
         optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Notes")), "note.png");
-        optgroup = page->new_optgroup(_(L("Notes")), 0);
+    page = add_options_page(L("Notes"), "note.png");
+        optgroup = page->new_optgroup(L("Notes"), 0);
         option = optgroup->get_option("notes");
         option.opt.full_width = true;
         option.opt.height = 25;//250;
         optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Dependencies")), "wrench.png");
-        optgroup = page->new_optgroup(_(L("Profile dependencies")));
+    page = add_options_page(L("Dependencies"), "wrench.png");
+        optgroup = page->new_optgroup(L("Profile dependencies"));
 
         create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) {
             return compatible_widget_create(parent, m_compatible_printers);
@@ -1484,8 +1490,8 @@ void TabPrint::OnActivate()
 
 void TabFilament::add_filament_overrides_page()
 {
-    PageShp page = add_options_page(_(L("Filament Overrides")), "wrench");
-    ConfigOptionsGroupShp optgroup = page->new_optgroup(_(L("Retraction")));
+    PageShp page = add_options_page(L("Filament Overrides"), "wrench");
+    ConfigOptionsGroupShp optgroup = page->new_optgroup(L("Retraction"));
 
     auto append_single_option_line = [optgroup, this](const std::string& opt_key, int opt_index)
     {
@@ -1539,12 +1545,12 @@ void TabFilament::add_filament_overrides_page()
 
 void TabFilament::update_filament_overrides_page()
 {
-    const auto page_it = std::find_if(m_pages.begin(), m_pages.end(), [](const PageShp page) {return page->title() == _(L("Filament Overrides")); });
+    const auto page_it = std::find_if(m_pages.begin(), m_pages.end(), [](const PageShp page) { return page->title() == "Filament Overrides"; });
     if (page_it == m_pages.end())
         return;
     PageShp page = *page_it;
 
-    const auto og_it = std::find_if(page->m_optgroups.begin(), page->m_optgroups.end(), [](const ConfigOptionsGroupShp og) {return og->title == _(L("Retraction")); });
+    const auto og_it = std::find_if(page->m_optgroups.begin(), page->m_optgroups.end(), [](const ConfigOptionsGroupShp og) { return og->title == "Retraction"; });
     if (og_it == page->m_optgroups.end())
         return;
     ConfigOptionsGroupShp optgroup = *og_it;
@@ -1586,27 +1592,28 @@ void TabFilament::build()
     m_presets = &m_preset_bundle->filaments;
     load_initial_data();
 
-    auto page = add_options_page(_(L("Filament")), "spool.png");
-        auto optgroup = page->new_optgroup(_(L("Filament")));
+    auto page = add_options_page(L("Filament"), "spool.png");
+        auto optgroup = page->new_optgroup(L("Filament"));
         optgroup->append_single_option_line("filament_colour");
         optgroup->append_single_option_line("filament_diameter");
         optgroup->append_single_option_line("extrusion_multiplier");
         optgroup->append_single_option_line("filament_density");
         optgroup->append_single_option_line("filament_cost");
 
-        optgroup = page->new_optgroup(_(L("Temperature")) + wxString(" °C", wxConvUTF8));
-        Line line = { _(L("Extruder")), "" };
+//        optgroup = page->new_optgroup(_(L("Temperature")) + wxString(" °C", wxConvUTF8));
+        optgroup = page->new_optgroup(L("Temperature"));
+        Line line = { L("Extruder"), "" };
         line.append_option(optgroup->get_option("first_layer_temperature"));
         line.append_option(optgroup->get_option("temperature"));
         optgroup->append_line(line);
 
-        line = { _(L("Bed")), "" };
+        line = { L("Bed"), "" };
         line.append_option(optgroup->get_option("first_layer_bed_temperature"));
         line.append_option(optgroup->get_option("bed_temperature"));
         optgroup->append_line(line);
 
-    page = add_options_page(_(L("Cooling")), "cooling");
-        optgroup = page->new_optgroup(_(L("Enable")));
+    page = add_options_page(L("Cooling"), "cooling");
+        optgroup = page->new_optgroup(L("Enable"));
         optgroup->append_single_option_line("fan_always_on");
         optgroup->append_single_option_line("cooling");
 
@@ -1617,8 +1624,8 @@ void TabFilament::build()
         };
         optgroup->append_line(line);
 
-        optgroup = page->new_optgroup(_(L("Fan settings")));
-        line = { _(L("Fan speed")), "" };
+        optgroup = page->new_optgroup(L("Fan settings"));
+        line = { L("Fan speed"), "" };
         line.append_option(optgroup->get_option("min_fan_speed"));
         line.append_option(optgroup->get_option("max_fan_speed"));
         optgroup->append_line(line);
@@ -1626,20 +1633,20 @@ void TabFilament::build()
         optgroup->append_single_option_line("bridge_fan_speed");
         optgroup->append_single_option_line("disable_fan_first_layers");
 
-        optgroup = page->new_optgroup(_(L("Cooling thresholds")), 25);
+        optgroup = page->new_optgroup(L("Cooling thresholds"), 25);
         optgroup->append_single_option_line("fan_below_layer_time");
         optgroup->append_single_option_line("slowdown_below_layer_time");
         optgroup->append_single_option_line("min_print_speed");
 
-    page = add_options_page(_(L("Advanced")), "wrench");
-        optgroup = page->new_optgroup(_(L("Filament properties")));
+    page = add_options_page(L("Advanced"), "wrench");
+        optgroup = page->new_optgroup(L("Filament properties"));
         // Set size as all another fields for a better alignment
         Option option = optgroup->get_option("filament_type");
         option.opt.width = Field::def_width();
         optgroup->append_single_option_line(option);
         optgroup->append_single_option_line("filament_soluble");
 
-        optgroup = page->new_optgroup(_(L("Print speed override")));
+        optgroup = page->new_optgroup(L("Print speed override"));
         optgroup->append_single_option_line("filament_max_volumetric_speed");
 
         line = { "", "" };
@@ -1649,10 +1656,10 @@ void TabFilament::build()
         };
         optgroup->append_line(line);
 
-        optgroup = page->new_optgroup(_(L("Wipe tower parameters")));
+        optgroup = page->new_optgroup(L("Wipe tower parameters"));
         optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
 
-        optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
+        optgroup = page->new_optgroup(L("Toolchange parameters with single extruder MM printers"));
         optgroup->append_single_option_line("filament_loading_speed_start");
         optgroup->append_single_option_line("filament_loading_speed");
         optgroup->append_single_option_line("filament_unloading_speed_start");
@@ -1688,29 +1695,29 @@ void TabFilament::build()
         const int gcode_field_height = 15; // 150
         const int notes_field_height = 25; // 250
 
-    page = add_options_page(_(L("Custom G-code")), "cog");
-        optgroup = page->new_optgroup(_(L("Start G-code")), 0);
+    page = add_options_page(L("Custom G-code"), "cog");
+        optgroup = page->new_optgroup(L("Start G-code"), 0);
         option = optgroup->get_option("start_filament_gcode");
         option.opt.full_width = true;
         option.opt.height = gcode_field_height;// 150;
         optgroup->append_single_option_line(option);
 
-        optgroup = page->new_optgroup(_(L("End G-code")), 0);
+        optgroup = page->new_optgroup(L("End G-code"), 0);
         option = optgroup->get_option("end_filament_gcode");
         option.opt.full_width = true;
         option.opt.height = gcode_field_height;// 150;
         optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Notes")), "note.png");
-        optgroup = page->new_optgroup(_(L("Notes")), 0);
+    page = add_options_page(L("Notes"), "note.png");
+        optgroup = page->new_optgroup(L("Notes"), 0);
         optgroup->label_width = 0;
         option = optgroup->get_option("filament_notes");
         option.opt.full_width = true;
         option.opt.height = notes_field_height;// 250;
         optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Dependencies")), "wrench.png");
-        optgroup = page->new_optgroup(_(L("Profile dependencies")));
+    page = add_options_page(L("Dependencies"), "wrench.png");
+        optgroup = page->new_optgroup(L("Profile dependencies"));
         create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) {
             return compatible_widget_create(parent, m_compatible_printers);
         });
@@ -1952,8 +1959,8 @@ void TabPrinter::build_fff()
     m_sys_extruders_count = parent_preset == nullptr ? 0 :
             static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size();
 
-    auto page = add_options_page(_(L("General")), "printer");
-        auto optgroup = page->new_optgroup(_(L("Size and coordinates")));
+    auto page = add_options_page(L("General"), "printer");
+        auto optgroup = page->new_optgroup(L("Size and coordinates"));
 
         create_line_with_widget(optgroup.get(), "bed_shape", [this](wxWindow* parent) {
             return 	create_bed_shape_widget(parent);
@@ -1962,7 +1969,7 @@ void TabPrinter::build_fff()
         optgroup->append_single_option_line("max_print_height");
         optgroup->append_single_option_line("z_offset");
 
-        optgroup = page->new_optgroup(_(L("Capabilities")));
+        optgroup = page->new_optgroup(L("Capabilities"));
         ConfigOptionDef def;
             def.type =  coInt,
             def.set_default_value(new ConfigOptionInt(1));
@@ -2072,10 +2079,10 @@ void TabPrinter::build_fff()
         }
 #endif
 
-        optgroup = page->new_optgroup(_(L("Print Host upload")));
+        optgroup = page->new_optgroup(L("Print Host upload"));
         build_printhost(optgroup.get());
 
-        optgroup = page->new_optgroup(_(L("Firmware")));
+        optgroup = page->new_optgroup(L("Firmware"));
         optgroup->append_single_option_line("gcode_flavor");
         optgroup->append_single_option_line("silent_mode");
         optgroup->append_single_option_line("remaining_times");
@@ -2095,7 +2102,7 @@ void TabPrinter::build_fff()
             });
         };
 
-        optgroup = page->new_optgroup(_(L("Advanced")));
+        optgroup = page->new_optgroup(L("Advanced"));
         optgroup->append_single_option_line("use_relative_e_distances");
         optgroup->append_single_option_line("use_firmware_retraction");
         optgroup->append_single_option_line("use_volumetric_e");
@@ -2103,52 +2110,52 @@ void TabPrinter::build_fff()
 
     const int gcode_field_height = 15; // 150
     const int notes_field_height = 25; // 250
-    page = add_options_page(_(L("Custom G-code")), "cog");
-        optgroup = page->new_optgroup(_(L("Start G-code")), 0);
+    page = add_options_page(L("Custom G-code"), "cog");
+        optgroup = page->new_optgroup(L("Start G-code"), 0);
         option = optgroup->get_option("start_gcode");
         option.opt.full_width = true;
         option.opt.height = gcode_field_height;//150;
         optgroup->append_single_option_line(option);
 
-        optgroup = page->new_optgroup(_(L("End G-code")), 0);
+        optgroup = page->new_optgroup(L("End G-code"), 0);
         option = optgroup->get_option("end_gcode");
         option.opt.full_width = true;
         option.opt.height = gcode_field_height;//150;
         optgroup->append_single_option_line(option);
 
-        optgroup = page->new_optgroup(_(L("Before layer change G-code")), 0);
+        optgroup = page->new_optgroup(L("Before layer change G-code"), 0);
         option = optgroup->get_option("before_layer_gcode");
         option.opt.full_width = true;
         option.opt.height = gcode_field_height;//150;
         optgroup->append_single_option_line(option);
 
-        optgroup = page->new_optgroup(_(L("After layer change G-code")), 0);
+        optgroup = page->new_optgroup(L("After layer change G-code"), 0);
         option = optgroup->get_option("layer_gcode");
         option.opt.full_width = true;
         option.opt.height = gcode_field_height;//150;
         optgroup->append_single_option_line(option);
 
-        optgroup = page->new_optgroup(_(L("Tool change G-code")), 0);
+        optgroup = page->new_optgroup(L("Tool change G-code"), 0);
         option = optgroup->get_option("toolchange_gcode");
         option.opt.full_width = true;
         option.opt.height = gcode_field_height;//150;
         optgroup->append_single_option_line(option);
 
-        optgroup = page->new_optgroup(_(L("Between objects G-code (for sequential printing)")), 0);
+        optgroup = page->new_optgroup(L("Between objects G-code (for sequential printing)"), 0);
         option = optgroup->get_option("between_objects_gcode");
         option.opt.full_width = true;
         option.opt.height = gcode_field_height;//150;
         optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Notes")), "note.png");
-        optgroup = page->new_optgroup(_(L("Notes")), 0);
+    page = add_options_page(L("Notes"), "note.png");
+        optgroup = page->new_optgroup(L("Notes"), 0);
         option = optgroup->get_option("printer_notes");
         option.opt.full_width = true;
         option.opt.height = notes_field_height;//250;
         optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Dependencies")), "wrench.png");
-        optgroup = page->new_optgroup(_(L("Profile dependencies")));
+    page = add_options_page(L("Dependencies"), "wrench.png");
+        optgroup = page->new_optgroup(L("Profile dependencies"));
 
         build_preset_description_line(optgroup.get());
 
@@ -2164,20 +2171,20 @@ void TabPrinter::build_sla()
 {
     if (!m_pages.empty())
         m_pages.resize(0);
-    auto page = add_options_page(_(L("General")), "printer");
-    auto optgroup = page->new_optgroup(_(L("Size and coordinates")));
+    auto page = add_options_page(L("General"), "printer");
+    auto optgroup = page->new_optgroup(L("Size and coordinates"));
 
     create_line_with_widget(optgroup.get(), "bed_shape", [this](wxWindow* parent) {
         return 	create_bed_shape_widget(parent);
     });
     optgroup->append_single_option_line("max_print_height");
 
-    optgroup = page->new_optgroup(_(L("Display")));
+    optgroup = page->new_optgroup(L("Display"));
     optgroup->append_single_option_line("display_width");
     optgroup->append_single_option_line("display_height");
 
     auto option = optgroup->get_option("display_pixels_x");
-    Line line = { _(option.opt.full_label), "" };
+    Line line = { option.opt.full_label, "" };
     line.append_option(option);
     line.append_option(optgroup->get_option("display_pixels_y"));
     optgroup->append_line(line);
@@ -2187,15 +2194,15 @@ void TabPrinter::build_sla()
     optgroup->append_single_option_line("display_mirror_x");
     optgroup->append_single_option_line("display_mirror_y");
 
-    optgroup = page->new_optgroup(_(L("Tilt")));
-    line = { _(L("Tilt time")), "" };
+    optgroup = page->new_optgroup(L("Tilt"));
+    line = { L("Tilt time"), "" };
     line.append_option(optgroup->get_option("fast_tilt_time"));
     line.append_option(optgroup->get_option("slow_tilt_time"));
     optgroup->append_line(line);
     optgroup->append_single_option_line("area_fill");
 
-    optgroup = page->new_optgroup(_(L("Corrections")));
-    line = Line{ _(m_config->def()->get("relative_correction")->full_label), "" };
+    optgroup = page->new_optgroup(L("Corrections"));
+    line = Line{ m_config->def()->get("relative_correction")->full_label, "" };
 //    std::vector<std::string> axes{ "X", "Y", "Z" };
     std::vector<std::string> axes{ "XY", "Z" };
     int id = 0;
@@ -2211,26 +2218,26 @@ void TabPrinter::build_sla()
     optgroup->append_single_option_line("elefant_foot_min_width");
     optgroup->append_single_option_line("gamma_correction");
     
-    optgroup = page->new_optgroup(_(L("Exposure")));
+    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")));
+    optgroup = page->new_optgroup(L("Print Host upload"));
     build_printhost(optgroup.get());
 
     const int notes_field_height = 25; // 250
 
-    page = add_options_page(_(L("Notes")), "note.png");
-    optgroup = page->new_optgroup(_(L("Notes")), 0);
+    page = add_options_page(L("Notes"), "note.png");
+    optgroup = page->new_optgroup(L("Notes"), 0);
     option = optgroup->get_option("printer_notes");
     option.opt.full_width = true;
     option.opt.height = notes_field_height;//250;
     optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Dependencies")), "wrench.png");
-    optgroup = page->new_optgroup(_(L("Profile dependencies")));
+    page = add_options_page(L("Dependencies"), "wrench.png");
+    optgroup = page->new_optgroup(L("Profile dependencies"));
 
     build_preset_description_line(optgroup.get());
 }
@@ -2269,7 +2276,7 @@ void TabPrinter::extruders_count_changed(size_t extruders_count)
 void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key)
 {
     auto option = optgroup->get_option(opt_key, 0);
-    auto line = Line{ _(option.opt.full_label), "" };
+    auto line = Line{ option.opt.full_label, "" };
     line.append_option(option);
     if (m_use_silent_mode)
         line.append_option(optgroup->get_option(opt_key, 1));
@@ -2278,7 +2285,7 @@ void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::s
 
 PageShp TabPrinter::build_kinematics_page()
 {
-    auto page = add_options_page(_(L("Machine limits")), "cog", true);
+    auto page = add_options_page(L("Machine limits"), "cog", true);
 
     if (m_use_silent_mode)	{
         // Legend for OptionsGroups
@@ -2307,24 +2314,24 @@ PageShp TabPrinter::build_kinematics_page()
     }
 
     std::vector<std::string> axes{ "x", "y", "z", "e" };
-    auto optgroup = page->new_optgroup(_(L("Maximum feedrates")));
+    auto optgroup = page->new_optgroup(L("Maximum feedrates"));
         for (const std::string &axis : axes)	{
             append_option_line(optgroup, "machine_max_feedrate_" + axis);
         }
 
-    optgroup = page->new_optgroup(_(L("Maximum accelerations")));
+    optgroup = page->new_optgroup(L("Maximum accelerations"));
         for (const std::string &axis : axes)	{
             append_option_line(optgroup, "machine_max_acceleration_" + axis);
         }
         append_option_line(optgroup, "machine_max_acceleration_extruding");
         append_option_line(optgroup, "machine_max_acceleration_retracting");
 
-    optgroup = page->new_optgroup(_(L("Jerk limits")));
+    optgroup = page->new_optgroup(L("Jerk limits"));
         for (const std::string &axis : axes)	{
             append_option_line(optgroup, "machine_max_jerk_" + axis);
         }
 
-    optgroup = page->new_optgroup(_(L("Minimum feedrates")));
+    optgroup = page->new_optgroup(L("Minimum feedrates"));
         append_option_line(optgroup, "machine_min_extruding_rate");
         append_option_line(optgroup, "machine_min_travel_rate");
 
@@ -2363,7 +2370,7 @@ void TabPrinter::build_unregular_pages()
     // Add/delete Kinematics page according to is_marlin_flavor
     size_t existed_page = 0;
     for (size_t i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already
-        if (m_pages[i]->title().find(_(L("Machine limits"))) != std::string::npos) {
+        if (m_pages[i]->title().find(L("Machine limits")) != std::string::npos) {
             if (!is_marlin_flavor || m_rebuild_kinematics_page)
                 m_pages.erase(m_pages.begin() + i);
             else
@@ -2388,7 +2395,7 @@ void TabPrinter::build_unregular_pages()
     {
         // if we have a single extruder MM setup, add a page with configuration options:
         for (size_t i = 0; i < m_pages.size(); ++i) // first make sure it's not there already
-            if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) {
+            if (m_pages[i]->title().find(L("Single extruder MM setup")) != std::string::npos) {
                 m_pages.erase(m_pages.begin() + i);
                 break;
             }
@@ -2396,8 +2403,8 @@ void TabPrinter::build_unregular_pages()
     }
     if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material") && !m_has_single_extruder_MM_page) {
         // create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
-        auto page = add_options_page(_(L("Single extruder MM setup")), "printer", true);
-        auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
+        auto page = add_options_page(L("Single extruder MM setup"), "printer", true);
+        auto optgroup = page->new_optgroup(L("Single extruder multimaterial parameters"));
         optgroup->append_single_option_line("cooling_tube_retraction");
         optgroup->append_single_option_line("cooling_tube_length");
         optgroup->append_single_option_line("parking_pos_retraction");
@@ -2410,11 +2417,11 @@ void TabPrinter::build_unregular_pages()
     // Build missed extruder pages
     for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx) {
         //# build page
-        const wxString& page_name = wxString::Format(_(L("Extruder %d")), int(extruder_idx + 1));
+        const wxString& page_name = wxString::Format(L("Extruder %d"), int(extruder_idx + 1));
         auto page = add_options_page(page_name, "funnel", true);
         m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page);
 
-            auto optgroup = page->new_optgroup(_(L("Size")));
+            auto optgroup = page->new_optgroup(L("Size"));
             optgroup->append_single_option_line("nozzle_diameter", extruder_idx);
 
             optgroup->m_on_change = [this, extruder_idx](const t_config_option_key& opt_key, boost::any value)
@@ -2452,18 +2459,18 @@ void TabPrinter::build_unregular_pages()
                 update();
             };
 
-            optgroup = page->new_optgroup(_(L("Layer height limits")));
+            optgroup = page->new_optgroup(L("Layer height limits"));
             optgroup->append_single_option_line("min_layer_height", extruder_idx);
             optgroup->append_single_option_line("max_layer_height", extruder_idx);
 
 
-            optgroup = page->new_optgroup(_(L("Position (for multi-extruder printers)")));
+            optgroup = page->new_optgroup(L("Position (for multi-extruder printers)"));
             optgroup->append_single_option_line("extruder_offset", extruder_idx);
 
-            optgroup = page->new_optgroup(_(L("Retraction")));
+            optgroup = page->new_optgroup(L("Retraction"));
             optgroup->append_single_option_line("retract_length", extruder_idx);
             optgroup->append_single_option_line("retract_lift", extruder_idx);
-                Line line = { _(L("Only lift Z")), "" };
+                Line line = { L("Only lift Z"), "" };
                 line.append_option(optgroup->get_option("retract_lift_above", extruder_idx));
                 line.append_option(optgroup->get_option("retract_lift_below", extruder_idx));
                 optgroup->append_line(line);
@@ -2476,11 +2483,11 @@ void TabPrinter::build_unregular_pages()
             optgroup->append_single_option_line("wipe", extruder_idx);
             optgroup->append_single_option_line("retract_before_wipe", extruder_idx);
 
-            optgroup = page->new_optgroup(_(L("Retraction when tool is disabled (advanced settings for multi-extruder setups)")));
+            optgroup = page->new_optgroup(L("Retraction when tool is disabled (advanced settings for multi-extruder setups)"));
             optgroup->append_single_option_line("retract_length_toolchange", extruder_idx);
             optgroup->append_single_option_line("retract_restart_extra_toolchange", extruder_idx);
 
-            optgroup = page->new_optgroup(_(L("Preview")));
+            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",
@@ -2799,7 +2806,7 @@ void Tab::rebuild_page_tree()
     m_treectrl->DeleteChildren(rootItem);
     for (auto p : m_pages)
     {
-        auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID());
+        auto itemId = m_treectrl->AppendItem(rootItem, _(p->title()), p->iconID());
         m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
         if (p->title() == selected) {
             m_treectrl->SelectItem(itemId);
@@ -2828,7 +2835,7 @@ void Tab::update_page_tree_visibility()
     {
         if (!p->get_show())
             continue;
-        auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID());
+        auto itemId = m_treectrl->AppendItem(rootItem, _(p->title()), p->iconID());
         m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
         if (p->title() == selected) {
             m_treectrl->SelectItem(itemId);
@@ -3087,7 +3094,7 @@ void Tab::OnTreeSelChange(wxTreeEvent& event)
     const auto sel_item = m_treectrl->GetSelection();
     const auto selection = sel_item ? m_treectrl->GetItemText(sel_item) : "";
     for (auto p : m_pages)
-        if (p->title() == selection)
+        if (_(p->title()) == selection)
         {
             page = p.get();
             m_is_nonsys_values = page->m_is_nonsys_values;
@@ -3536,6 +3543,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
 
     //! config_ have to be "right"
     ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(this, title, m_config, true, extra_column);
+    optgroup->config_category = m_title.ToStdString();
     if (noncommon_label_width >= 0)
         optgroup->label_width = noncommon_label_width;
 
@@ -3646,9 +3654,9 @@ void TabSLAMaterial::build()
     m_presets = &m_preset_bundle->sla_materials;
     load_initial_data();
 
-    auto page = add_options_page(_(L("Material")), "resin");
+    auto page = add_options_page(L("Material"), "resin");
 
-    auto optgroup = page->new_optgroup(_(L("Material")));
+    auto optgroup = page->new_optgroup(L("Material"));
     optgroup->append_single_option_line("bottle_cost");
     optgroup->append_single_option_line("bottle_volume");
     optgroup->append_single_option_line("bottle_weight");
@@ -3680,19 +3688,19 @@ void TabSLAMaterial::build()
         wxGetApp().sidebar().Layout();
     };
 
-    optgroup = page->new_optgroup(_(L("Layers")));
+    optgroup = page->new_optgroup(L("Layers"));
     optgroup->append_single_option_line("initial_layer_height");
 
-    optgroup = page->new_optgroup(_(L("Exposure")));
+    optgroup = page->new_optgroup(L("Exposure"));
     optgroup->append_single_option_line("exposure_time");
     optgroup->append_single_option_line("initial_exposure_time");
 
-    optgroup = page->new_optgroup(_(L("Corrections")));
+    optgroup = page->new_optgroup(L("Corrections"));
     std::vector<std::string> corrections = {"material_correction"};
 //    std::vector<std::string> axes{ "X", "Y", "Z" };
     std::vector<std::string> axes{ "XY", "Z" };
     for (auto& opt_key : corrections) {
-        auto line = Line{ _(m_config->def()->get(opt_key)->full_label), "" };
+        auto line = Line{ m_config->def()->get(opt_key)->full_label, "" };
         int id = 0;
         for (auto& axis : axes) {
             auto opt = optgroup->get_option(opt_key, id);
@@ -3703,16 +3711,16 @@ void TabSLAMaterial::build()
         optgroup->append_line(line);
     }
 
-    page = add_options_page(_(L("Notes")), "note.png");
-    optgroup = page->new_optgroup(_(L("Notes")), 0);
+    page = add_options_page(L("Notes"), "note.png");
+    optgroup = page->new_optgroup(L("Notes"), 0);
     optgroup->label_width = 0;
     Option option = optgroup->get_option("material_notes");
     option.opt.full_width = true;
     option.opt.height = 25;//250;
     optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Dependencies")), "wrench.png");
-    optgroup = page->new_optgroup(_(L("Profile dependencies")));
+    page = add_options_page(L("Dependencies"), "wrench.png");
+    optgroup = page->new_optgroup(L("Profile dependencies"));
 
     create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) {
         return compatible_widget_create(parent, m_compatible_printers);
@@ -3760,22 +3768,22 @@ void TabSLAPrint::build()
     m_presets = &m_preset_bundle->sla_prints;
     load_initial_data();
 
-    auto page = add_options_page(_(L("Layers and perimeters")), "layers");
+    auto page = add_options_page(L("Layers and perimeters"), "layers");
 
-    auto optgroup = page->new_optgroup(_(L("Layers")));
+    auto optgroup = page->new_optgroup(L("Layers"));
     optgroup->append_single_option_line("layer_height");
     optgroup->append_single_option_line("faded_layers");
 
-    page = add_options_page(_(L("Supports")), "support"/*"sla_supports"*/);
-    optgroup = page->new_optgroup(_(L("Supports")));
+    page = add_options_page(L("Supports"), "support"/*"sla_supports"*/);
+    optgroup = page->new_optgroup(L("Supports"));
     optgroup->append_single_option_line("supports_enable");
 
-    optgroup = page->new_optgroup(_(L("Support head")));
+    optgroup = page->new_optgroup(L("Support head"));
     optgroup->append_single_option_line("support_head_front_diameter");
     optgroup->append_single_option_line("support_head_penetration");
     optgroup->append_single_option_line("support_head_width");
 
-    optgroup = page->new_optgroup(_(L("Support pillar")));
+    optgroup = page->new_optgroup(L("Support pillar"));
     optgroup->append_single_option_line("support_pillar_diameter");
     optgroup->append_single_option_line("support_max_bridges_on_pillar");
     
@@ -3791,17 +3799,17 @@ void TabSLAPrint::build()
     optgroup->append_single_option_line("pad_around_object");
     optgroup->append_single_option_line("support_object_elevation");
 
-    optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions")));
+    optgroup = page->new_optgroup(L("Connection of the support sticks and junctions"));
     optgroup->append_single_option_line("support_critical_angle");
     optgroup->append_single_option_line("support_max_bridge_length");
     optgroup->append_single_option_line("support_max_pillar_link_distance");
 
-    optgroup = page->new_optgroup(_(L("Automatic generation")));
+    optgroup = page->new_optgroup(L("Automatic generation"));
     optgroup->append_single_option_line("support_points_density_relative");
     optgroup->append_single_option_line("support_points_minimal_distance");
 
-    page = add_options_page(_(L("Pad")), "pad");
-    optgroup = page->new_optgroup(_(L("Pad")));
+    page = add_options_page(L("Pad"), "pad");
+    optgroup = page->new_optgroup(L("Pad"));
     optgroup->append_single_option_line("pad_enable");
     optgroup->append_single_option_line("pad_wall_thickness");
     optgroup->append_single_option_line("pad_wall_height");
@@ -3818,25 +3826,25 @@ void TabSLAPrint::build()
     optgroup->append_single_option_line("pad_object_connector_width");
     optgroup->append_single_option_line("pad_object_connector_penetration");
     
-    page = add_options_page(_(L("Hollowing")), "hollowing");
-    optgroup = page->new_optgroup(_(L("Hollowing")));
+    page = add_options_page(L("Hollowing"), "hollowing");
+    optgroup = page->new_optgroup(L("Hollowing"));
     optgroup->append_single_option_line("hollowing_enable");
     optgroup->append_single_option_line("hollowing_min_thickness");
     optgroup->append_single_option_line("hollowing_quality");
     optgroup->append_single_option_line("hollowing_closing_distance");
 
-    page = add_options_page(_(L("Advanced")), "wrench");
-    optgroup = page->new_optgroup(_(L("Slicing")));
+    page = add_options_page(L("Advanced"), "wrench");
+    optgroup = page->new_optgroup(L("Slicing"));
     optgroup->append_single_option_line("slice_closing_radius");
 
-    page = add_options_page(_(L("Output options")), "output+page_white");
-    optgroup = page->new_optgroup(_(L("Output file")));
+    page = add_options_page(L("Output options"), "output+page_white");
+    optgroup = page->new_optgroup(L("Output file"));
     Option option = optgroup->get_option("output_filename_format");
     option.opt.full_width = true;
     optgroup->append_single_option_line(option);
 
-    page = add_options_page(_(L("Dependencies")), "wrench");
-    optgroup = page->new_optgroup(_(L("Profile dependencies")));
+    page = add_options_page(L("Dependencies"), "wrench");
+    optgroup = page->new_optgroup(L("Profile dependencies"));
 
     create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) {
         return compatible_widget_create(parent, m_compatible_printers);
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index e16b44819..3bce5de6c 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -50,7 +50,7 @@ class Page : public wxScrolledWindow
 	wxBoxSizer*		m_vsizer;
     bool            m_show = true;
 public:
-    Page(wxWindow* parent, const wxString title, const int iconID, const std::vector<ScalableBitmap>& mode_bmp_cache) :
+    Page(wxWindow* parent, const wxString& title, const int iconID, const std::vector<ScalableBitmap>& mode_bmp_cache) :
 			m_parent(parent),
 			m_title(title),
 			m_iconID(iconID),
@@ -122,8 +122,7 @@ protected:
 	std::string			m_name;
 	const wxString		m_title;
 	PresetBitmapComboBox*	m_presets_choice;
-//	SearchComboBox*		m_search_cb;
-	SearchCtrl*			m_search;
+	Search::SearchCtrl*	m_search;
 	ScalableButton*		m_btn_save_preset;
 	ScalableButton*		m_btn_delete_preset;
 	ScalableButton*		m_btn_hide_incompatible_presets;
diff --git a/src/slic3r/GUI/fts_fuzzy_match.h b/src/slic3r/GUI/fts_fuzzy_match.h
index 046027e16..da4b5d2a0 100644
--- a/src/slic3r/GUI/fts_fuzzy_match.h
+++ b/src/slic3r/GUI/fts_fuzzy_match.h
@@ -181,12 +181,14 @@ namespace fts {
                 // Check for bonuses based on neighbor character value
                 if (currIdx > 0) {
                     // Camel case
-                    char neighbor = strBegin[currIdx - 1];
-                    char curr = strBegin[currIdx];
-                    if (::islower(neighbor) && ::isupper(curr))
+                    // ::islower() expects an unsigned char in range of 0 to 255.
+                    unsigned char uneighbor = ((unsigned char *)strBegin)[currIdx - 1];
+                    unsigned char ucurr = ((unsigned char*)strBegin)[currIdx];
+                    if (::islower(uneighbor) && ::isupper(ucurr))
                         outScore += camel_bonus;
 
                     // Separator
+                    char neighbor = strBegin[currIdx - 1];
                     bool neighborSeparator = neighbor == '_' || neighbor == ' ';
                     if (neighborSeparator)
                         outScore += separator_bonus;

From 10110ed3075b2b2c50096e842a45ea456c8106e2 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 14 Apr 2020 11:53:28 +0200
Subject: [PATCH 24/55] WIP: Ironing over top surfaces.

---
 src/libslic3r/ExtrusionEntity.cpp     |   1 +
 src/libslic3r/ExtrusionEntity.hpp     |   7 +-
 src/libslic3r/Fill/Fill.cpp           | 164 +++++++++++++++++++++++++-
 src/libslic3r/Fill/FillBase.hpp       |  14 +--
 src/libslic3r/GCode.cpp               |  42 ++++---
 src/libslic3r/GCode.hpp               |   2 +-
 src/libslic3r/GCode/PreviewData.cpp   |   1 +
 src/libslic3r/Layer.hpp               |   6 +-
 src/libslic3r/Print.cpp               |   2 +
 src/libslic3r/Print.hpp               |   3 +-
 src/libslic3r/PrintConfig.cpp         |  47 ++++++++
 src/libslic3r/PrintConfig.hpp         |  28 +++++
 src/libslic3r/PrintObject.cpp         |  19 +++
 src/slic3r/GUI/ConfigManipulation.cpp |   4 +
 src/slic3r/GUI/Field.cpp              |   2 +
 src/slic3r/GUI/GUI.cpp                |   2 +
 src/slic3r/GUI/OptionsGroup.cpp       |   3 +
 src/slic3r/GUI/Preset.cpp             |   5 +-
 src/slic3r/GUI/Tab.cpp                |   7 ++
 19 files changed, 321 insertions(+), 38 deletions(-)

diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp
index c0d08c84b..c482f7edb 100644
--- a/src/libslic3r/ExtrusionEntity.cpp
+++ b/src/libslic3r/ExtrusionEntity.cpp
@@ -312,6 +312,7 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
         case erOverhangPerimeter            : return L("Overhang perimeter");
         case erInternalInfill               : return L("Internal infill");
         case erSolidInfill                  : return L("Solid infill");
+        case erIroning                      : return L("Ironing");
         case erTopSolidInfill               : return L("Top solid infill");
         case erBridgeInfill                 : return L("Bridge infill");
         case erGapFill                      : return L("Gap fill");
diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp
index b76991f1c..879f564b6 100644
--- a/src/libslic3r/ExtrusionEntity.hpp
+++ b/src/libslic3r/ExtrusionEntity.hpp
@@ -22,6 +22,7 @@ enum ExtrusionRole : uint8_t {
     erInternalInfill,
     erSolidInfill,
     erTopSolidInfill,
+    erIroning,
     erBridgeInfill,
     erGapFill,
     erSkirt,
@@ -54,14 +55,16 @@ inline bool is_infill(ExtrusionRole role)
     return role == erBridgeInfill
         || role == erInternalInfill
         || role == erSolidInfill
-        || role == erTopSolidInfill;
+        || role == erTopSolidInfill
+        || role == erIroning;
 }
 
 inline bool is_solid_infill(ExtrusionRole role)
 {
     return role == erBridgeInfill
         || role == erSolidInfill
-        || role == erTopSolidInfill;
+        || role == erTopSolidInfill
+        || role == erIroning;
 }
 
 inline bool is_bridge(ExtrusionRole role) {
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index 498abe89e..f62b3ab25 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -10,6 +10,7 @@
 #include "../Surface.hpp"
 
 #include "FillBase.hpp"
+#include "FillRectilinear2.hpp"
 
 namespace Slic3r {
 
@@ -388,8 +389,8 @@ void Layer::make_fills()
 		        	flow_width      = new_flow.width;
 		        }
 		        // Save into layer.
-		        auto *eec = new ExtrusionEntityCollection();
-		        m_regions[surface_fill.region_id]->fills.entities.push_back(eec);
+				ExtrusionEntityCollection* eec = nullptr;
+		        m_regions[surface_fill.region_id]->fills.entities.push_back(eec = new ExtrusionEntityCollection());
 		        // Only concentric fills are not sorted.
 		        eec->no_sort = f->no_sort();
 		        extrusion_entities_append_paths(
@@ -418,4 +419,163 @@ void Layer::make_fills()
 #endif
 }
 
+// Create ironing extrusions over top surfaces.
+void Layer::make_ironing()
+{
+	// LayerRegion::slices contains surfaces marked with SurfaceType.
+	// Here we want to collect top surfaces extruded with the same extruder.
+	// A surface will be ironed with the same extruder to not contaminate the print with another material leaking from the nozzle.
+
+	// First classify regions based on the extruder used.
+	struct IroningParams {
+		int 		extruder 	= -1;
+		bool 		just_infill = false;
+		// Spacing of the ironing lines, also to calculate the extrusion flow from.
+		double 		line_spacing;
+		// Height of the extrusion, to calculate the extrusion flow from.
+		double 		height;
+		double 		speed;
+		double 		angle;
+
+		bool operator<(const IroningParams &rhs) const {
+			if (this->extruder < rhs.extruder)
+				return true;
+			if (this->extruder > rhs.extruder)
+				return false;
+			if (int(this->just_infill) < int(rhs.just_infill))
+				return true;
+			if (int(this->just_infill) > int(rhs.just_infill))
+				return false;
+			if (this->line_spacing < rhs.line_spacing)
+				return true;
+			if (this->line_spacing > rhs.line_spacing)
+				return false;
+			if (this->height < rhs.height)
+				return true;
+			if (this->height > rhs.height)
+				return false;
+			if (this->speed < rhs.speed)
+				return true;
+			if (this->speed > rhs.speed)
+				return false;
+			if (this->angle < rhs.angle)
+				return true;
+			if (this->angle > rhs.angle)
+				return false;
+			return false;
+		}
+
+		bool operator==(const IroningParams &rhs) const {
+			return this->extruder == rhs.extruder && this->just_infill == rhs.just_infill &&
+				   this->line_spacing == rhs.line_spacing && this->height == rhs.height && this->speed == rhs.speed &&
+				   this->angle == rhs.angle;
+		}
+
+		LayerRegion *layerm		= nullptr;
+
+		// IdeaMaker: ironing
+		// ironing flowrate (5% percent)
+		// ironing speed (10 mm/sec)
+
+		// Kisslicer: 
+		// iron off, Sweep, Group
+		// ironing speed: 15 mm/sec
+
+		// Cura:
+		// Pattern (zig-zag / concentric)
+		// line spacing (0.1mm)
+		// flow: from normal layer height. 10%
+		// speed: 20 mm/sec
+	};
+
+	std::vector<IroningParams> by_extruder;
+	bool   extruder_dont_care   = this->object()->config().wipe_into_objects;
+    double default_layer_height = this->object()->config().layer_height;
+
+	for (LayerRegion *layerm : m_regions)
+		if (! layerm->slices.empty()) {
+			IroningParams ironing_params;
+			const PrintRegionConfig &config = layerm->region()->config();
+			if (config.ironing_type == IroningType::AllSolid ||
+				(config.top_solid_layers > 0 && 
+					(config.ironing_type == IroningType::TopSurfaces ||
+					 (config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr)))) {
+				if (config.perimeter_extruder == config.solid_infill_extruder || config.perimeters == 0) {
+					// Iron the whole face.
+					ironing_params.extruder = config.solid_infill_extruder;
+				} else {
+					// Iron just the infill.
+					ironing_params.extruder = config.solid_infill_extruder;
+				}
+			}
+			if (ironing_params.extruder != -1) {
+				ironing_params.just_infill 	= false;
+				ironing_params.line_spacing = config.ironing_spacing;
+				ironing_params.height 		= default_layer_height * 0.01 * config.ironing_flowrate;
+				ironing_params.speed 		= config.ironing_speed;
+				ironing_params.angle 		= config.fill_angle * M_PI / 180.;
+				ironing_params.layerm 		= layerm;
+				by_extruder.emplace_back(ironing_params);
+			}
+		}
+	std::sort(by_extruder.begin(), by_extruder.end());
+
+    FillRectilinear2 	fill;
+    FillParams 			fill_params;
+	fill.set_bounding_box(this->object()->bounding_box());
+	fill.layer_id 			 = this->id();
+    fill.z 					 = this->print_z;
+    fill.overlap 			 = 0;
+    fill_params.density 	 = 1.;
+    fill_params.dont_connect = true;
+
+	for (size_t i = 0; i < by_extruder.size(); ++ i) {
+		// Find span of regions equivalent to the ironing operation.
+		IroningParams &ironing_params = by_extruder[i];
+		size_t j = i;
+		for (++ j; j < by_extruder.size() && ironing_params == by_extruder[j]; ++ j) ;
+
+		// Create the ironing extrusions for regions <i, j)
+		ExPolygons ironing_areas;
+		double nozzle_dmr = this->object()->print()->config().nozzle_diameter.values[ironing_params.extruder - 1];
+		if (ironing_params.just_infill) {
+			// Just infill.
+		} else {
+			// Infill and perimeter.
+			// Merge top surfaces with the same ironing parameters.
+			Polygons polys;
+			for (size_t k = i; k < j; ++ k)
+				for (const Surface &surface : by_extruder[k].layerm->slices.surfaces)
+					if (surface.surface_type == stTop)
+						polygons_append(polys, surface.expolygon);
+			// Trim the top surfaces with half the nozzle diameter.
+			ironing_areas = intersection_ex(polys, offset(this->lslices, - float(scale_(0.5 * nozzle_dmr))));
+		}
+
+        // Create the filler object.
+        fill.spacing = ironing_params.line_spacing;
+        fill.angle = float(ironing_params.angle + 0.25 * M_PI);
+        fill.link_max_length = (coord_t)scale_(3. * fill.spacing);
+		double height = ironing_params.height * fill.spacing / nozzle_dmr;
+        Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height), false);
+        double flow_mm3_per_mm = flow.mm3_per_mm();
+        Surface surface_fill(stTop, ExPolygon());
+        for (ExPolygon &expoly : ironing_areas) {
+			surface_fill.expolygon = std::move(expoly);
+			Polylines polylines = fill.fill_surface(&surface_fill, fill_params);
+	        if (! polylines.empty()) {
+		        // Save into layer.
+				ExtrusionEntityCollection *eec = nullptr;
+		        ironing_params.layerm->fills.entities.push_back(eec = new ExtrusionEntityCollection());
+		        //FIXME we may not want to sort a monotonous infill.
+		        eec->no_sort = false;
+		        extrusion_entities_append_paths(
+		            eec->entities, std::move(polylines),
+		            erIroning,
+		            flow_mm3_per_mm, float(flow.width), float(height));
+		    }
+		}
+	}
+}
+
 } // namespace Slic3r
diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp
index 517ce8383..5a9e92739 100644
--- a/src/libslic3r/Fill/FillBase.hpp
+++ b/src/libslic3r/Fill/FillBase.hpp
@@ -20,27 +20,21 @@ class Surface;
 
 struct FillParams
 {
-    FillParams() { 
-        memset(this, 0, sizeof(FillParams));
-        // Adjustment does not work.
-        dont_adjust = true;
-    }
-
     bool        full_infill() const { return density > 0.9999f; }
 
     // Fill density, fraction in <0, 1>
-    float       density;
+    float       density 		{ 0.f };
 
     // Don't connect the fill lines around the inner perimeter.
-    bool        dont_connect;
+    bool        dont_connect 	{ false };
 
     // Don't adjust spacing to fill the space evenly.
-    bool        dont_adjust;
+    bool        dont_adjust 	{ true };
 
     // For Honeycomb.
     // we were requested to complete each loop;
     // in this case we don't try to make more continuous paths
-    bool        complete;
+    bool        complete 		{ false };
 };
 static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
 
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 3a90349b5..01a804ee7 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -2246,12 +2246,14 @@ void GCode::process_layer(
                     const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, static_cast<unsigned int>(instance_to_print.instance_id), extruder_id, print_wipe_extrusions != 0) : island.by_region;
                 	//FIXME the following code prints regions in the order they are defined, the path is not optimized in any way.
                     if (print.config().infill_first) {
-                        gcode += this->extrude_infill(print, by_region_specific);
+                        gcode += this->extrude_infill(print, by_region_specific, false);
                         gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
                     } else {
                         gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
-                        gcode += this->extrude_infill(print,by_region_specific);
+                        gcode += this->extrude_infill(print,by_region_specific, false);
                     }
+                    // ironing
+                    gcode += this->extrude_infill(print,by_region_specific, true);
                 }
                 if (this->config().gcode_label_objects)
 					gcode += std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
@@ -2873,22 +2875,30 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
 }
 
 // Chain the paths hierarchically by a greedy algorithm to minimize a travel distance.
-std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region)
+std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing)
 {
-    std::string gcode;
+    std::string 		 gcode;
+    ExtrusionEntitiesPtr extrusions;
+    const char*          extrusion_name = ironing ? "ironing" : "infill";
     for (const ObjectByExtruder::Island::Region &region : by_region)
         if (! region.infills.empty()) {
-            m_config.apply(print.regions()[&region - &by_region.front()]->config());
-		    ExtrusionEntitiesPtr extrusions { region.infills };
-		    chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
-            for (const ExtrusionEntity *fill : extrusions) {
-                auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
-                if (eec) {
-				    for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities)
-                        gcode += this->extrude_entity(*ee, "infill");
-                } else
-                    gcode += this->extrude_entity(*fill, "infill");
-            }
+        	extrusions.clear();
+        	extrusions.reserve(region.infills.size());
+        	for (ExtrusionEntity *ee : region.infills)
+        		if ((ee->role() == erIroning) == ironing)
+        			extrusions.emplace_back(ee);
+        	if (! extrusions.empty()) {
+	            m_config.apply(print.regions()[&region - &by_region.front()]->config());
+			    chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
+	            for (const ExtrusionEntity *fill : extrusions) {
+	                auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
+	                if (eec) {
+					    for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities)
+	                        gcode += this->extrude_entity(*ee, extrusion_name);
+	                } else
+	                    gcode += this->extrude_entity(*fill, extrusion_name);
+	            }
+	        }
         }
     return gcode;
 }
@@ -3027,6 +3037,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
             speed = m_config.get_abs_value("solid_infill_speed");
         } else if (path.role() == erTopSolidInfill) {
             speed = m_config.get_abs_value("top_solid_infill_speed");
+        } else if (path.role() == erIroning) {
+            speed = m_config.get_abs_value("ironing_speed");
         } else if (path.role() == erGapFill) {
             speed = m_config.get_abs_value("gap_fill_speed");
         } else {
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 7fc75c92b..2daf0fe16 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -295,7 +295,7 @@ private:
 		const size_t                     				 single_object_instance_idx);
 
     std::string     extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
-    std::string     extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
+    std::string     extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing);
     std::string     extrude_support(const ExtrusionEntityCollection &support_fills);
 
     std::string     travel_to(const Point &point, ExtrusionRole role, std::string comment);
diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp
index 8a9184e64..3aae15748 100644
--- a/src/libslic3r/GCode/PreviewData.cpp
+++ b/src/libslic3r/GCode/PreviewData.cpp
@@ -117,6 +117,7 @@ const Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount]
     Color(1.0f, 1.0f, 0.0f, 1.0f),   // erInternalInfill
     Color(1.0f, 0.0f, 1.0f, 1.0f),   // erSolidInfill
     Color(0.0f, 1.0f, 1.0f, 1.0f),   // erTopSolidInfill
+    Color(0.0f, 1.0f, 1.0f, 1.0f),   // erIroning    
     Color(0.5f, 0.5f, 0.5f, 1.0f),   // erBridgeInfill
     Color(1.0f, 1.0f, 1.0f, 1.0f),   // erGapFill
     Color(0.5f, 0.0f, 0.0f, 1.0f),   // erSkirt
diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp
index d66aa8f01..54e4baf2c 100644
--- a/src/libslic3r/Layer.hpp
+++ b/src/libslic3r/Layer.hpp
@@ -36,11 +36,6 @@ public:
     // collection of surfaces for infill generation
     SurfaceCollection           fill_surfaces;
 
-    // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces).
-    // While not necessary, the memory consumption is meager and it speeds up calculation.
-    // The perimeter_surfaces keep the IDs of the slices (top/bottom/)
-    SurfaceCollection           perimeter_surfaces;
-
     // collection of expolygons representing the bridged areas (thus not
     // needing support material)
     Polygons                    bridged;
@@ -140,6 +135,7 @@ public:
     }
     void                    make_perimeters();
     void                    make_fills();
+    void 					make_ironing();
 
     void                    export_region_slices_to_svg(const char *path) const;
     void                    export_region_fill_surfaces_to_svg(const char *path) const;
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 8631db624..986b7fa09 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1583,6 +1583,8 @@ void Print::process()
     this->set_status(70, L("Infilling layers"));
     for (PrintObject *obj : m_objects)
         obj->infill();
+    for (PrintObject *obj : m_objects)
+        obj->ironing();
     for (PrintObject *obj : m_objects)
         obj->generate_support_material();
     if (this->set_started(psWipeTower)) {
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 7180bae17..d4d3a1ddc 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -41,7 +41,7 @@ enum PrintStep {
 
 enum PrintObjectStep {
     posSlice, posPerimeters, posPrepareInfill,
-    posInfill, posSupportMaterial, posCount,
+    posInfill, posIroning, posSupportMaterial, posCount,
 };
 
 // A PrintRegion object represents a group of volumes to print
@@ -218,6 +218,7 @@ private:
     void make_perimeters();
     void prepare_infill();
     void infill();
+    void ironing();
     void generate_support_material();
 
     void _slice(const std::vector<coordf_t> &layer_height_profile);
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index c7a7a9c8e..259b016e3 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -1076,6 +1076,53 @@ void PrintConfigDef::init_fff_params()
     def->mode = comExpert;
     def->set_default_value(new ConfigOptionBool(false));
 
+    def = this->add("ironing", coBool);
+    def->label = L("Enable ironing");
+    def->tooltip = L("Enable ironing of the top layers with the hot print head for smooth surface");
+    def->category = L("Ironing");
+    def->mode = comAdvanced;
+    def->set_default_value(new ConfigOptionBool(false));
+
+    def = this->add("ironing_type", coEnum);
+    def->label = L("Ironingy Type");
+    def->tooltip = L("Ironingy Type");
+    def->enum_keys_map = &ConfigOptionEnum<IroningType>::get_enum_values();
+    def->enum_values.push_back("top");
+    def->enum_values.push_back("topmost");
+    def->enum_values.push_back("solid");
+    def->enum_labels.push_back("All top surfaces");
+    def->enum_labels.push_back("Topmost surface only");
+    def->enum_labels.push_back("All solid surfaces");
+    def->mode = comAdvanced;
+    def->set_default_value(new ConfigOptionEnum<IroningType>(IroningType::TopSurfaces));
+
+    def = this->add("ironing_flowrate", coPercent);
+    def->label = L("Flow rate");
+    def->category = L("Ironing");
+    def->tooltip = L("Percent of a flow rate relative to object's normal layer height.");
+    def->sidetext = L("%");
+    def->ratio_over = "layer_height";
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionPercent(15));
+
+    def = this->add("ironing_spacing", coFloat);
+    def->label = L("Spacing between ironing passes");
+    def->tooltip = L("Distance between ironing lins");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(0.1));
+
+    def = this->add("ironing_speed", coFloat);
+    def->label = L("Ironing speed");
+    def->category = L("Speed");
+    def->tooltip = L("Ironing speed");
+    def->sidetext = L("mm/s");
+    def->min = 0;
+    def->mode = comAdvanced;
+    def->set_default_value(new ConfigOptionFloat(60));
+
     def = this->add("layer_gcode", coString);
     def->label = L("After layer change G-code");
     def->tooltip = L("This custom code is inserted at every layer change, right after the Z move "
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index ca509e37a..a7d2d8270 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -38,6 +38,13 @@ enum InfillPattern {
     ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount,
 };
 
+enum class IroningType {
+	TopSurfaces,
+	TopmostOnly,
+	AllSolid,
+	Count,
+};
+
 enum SupportMaterialPattern {
     smpRectilinear, smpRectilinearGrid, smpHoneycomb,
 };
@@ -122,6 +129,16 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::g
     return keys_map;
 }
 
+template<> inline const t_config_enum_values& ConfigOptionEnum<IroningType>::get_enum_values() {
+    static t_config_enum_values keys_map;
+    if (keys_map.empty()) {
+        keys_map["top"]                 = int(IroningType::TopSurfaces);
+        keys_map["topmost"]             = int(IroningType::TopmostOnly);
+        keys_map["solid"]               = int(IroningType::AllSolid);
+    }
+    return keys_map;
+}
+
 template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
@@ -485,6 +502,12 @@ public:
     ConfigOptionInt                 infill_every_layers;
     ConfigOptionFloatOrPercent      infill_overlap;
     ConfigOptionFloat               infill_speed;
+    // Ironing options
+    ConfigOptionBool 				ironing;
+    ConfigOptionEnum<IroningType> 	ironing_type;
+    ConfigOptionPercent 			ironing_flowrate;
+    ConfigOptionFloat 				ironing_spacing;
+    ConfigOptionFloat 				ironing_speed;
     // Detect bridging perimeters
     ConfigOptionBool                overhangs;
     ConfigOptionInt                 perimeter_extruder;
@@ -530,6 +553,11 @@ protected:
         OPT_PTR(infill_every_layers);
         OPT_PTR(infill_overlap);
         OPT_PTR(infill_speed);
+        OPT_PTR(ironing);
+        OPT_PTR(ironing_type);
+        OPT_PTR(ironing_flowrate);
+        OPT_PTR(ironing_spacing);
+        OPT_PTR(ironing_speed);
         OPT_PTR(overhangs);
         OPT_PTR(perimeter_extruder);
         OPT_PTR(perimeter_extrusion_width);
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 5573f4ac3..a68a9605b 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -387,6 +387,25 @@ void PrintObject::infill()
     }
 }
 
+void PrintObject::ironing()
+{
+    if (this->set_started(posIroning)) {
+        BOOST_LOG_TRIVIAL(debug) << "Ironing in parallel - start";
+        tbb::parallel_for(
+            tbb::blocked_range<size_t>(1, m_layers.size()),
+            [this](const tbb::blocked_range<size_t>& range) {
+                for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
+                    m_print->throw_if_canceled();
+                    m_layers[layer_idx]->make_ironing();
+                }
+            }
+        );
+        m_print->throw_if_canceled();
+        BOOST_LOG_TRIVIAL(debug) << "Ironing in parallel - end";
+        this->set_done(posIroning);
+    }
+}
+
 void PrintObject::generate_support_material()
 {
     if (this->set_started(posSupportMaterial)) {
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index 8d1daeb8e..d7f0a37b0 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -298,6 +298,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
     toggle_field("support_material_extruder", have_support_material || have_skirt);
     toggle_field("support_material_speed", have_support_material || have_brim || have_skirt);
 
+    bool has_ironing = config->opt_bool("ironing");
+    for (auto el : { "ironing_type", "ironing_flowrate", "ironing_spacing", "ironing_speed" })
+    	toggle_field(el, has_ironing);
+
     bool have_sequential_printing = config->opt_bool("complete_objects");
     for (auto el : { "extruder_clearance_radius", "extruder_clearance_height" })
         toggle_field(el, have_sequential_printing);
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index a1a6583bc..12699c37f 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -1027,6 +1027,8 @@ boost::any& Choice::get_value()
 		}
 		if (m_opt_id.compare("fill_pattern") == 0)
 			m_value = static_cast<InfillPattern>(ret_enum);
+		else if (m_opt_id.compare("ironing_type") == 0)
+			m_value = static_cast<IroningType>(ret_enum);
 		else if (m_opt_id.compare("gcode_flavor") == 0)
 			m_value = static_cast<GCodeFlavor>(ret_enum);
 		else if (m_opt_id.compare("support_material_pattern") == 0)
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index caeb8da03..7deed0786 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -188,6 +188,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
 				opt_key == "bottom_fill_pattern" ||
 				opt_key == "fill_pattern")
 				config.set_key_value(opt_key, new ConfigOptionEnum<InfillPattern>(boost::any_cast<InfillPattern>(value))); 
+			else if (opt_key.compare("ironing_type") == 0)
+				config.set_key_value(opt_key, new ConfigOptionEnum<IroningType>(boost::any_cast<IroningType>(value))); 
 			else if (opt_key.compare("gcode_flavor") == 0)
 				config.set_key_value(opt_key, new ConfigOptionEnum<GCodeFlavor>(boost::any_cast<GCodeFlavor>(value))); 
 			else if (opt_key.compare("support_material_pattern") == 0)
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index 207d42b5b..d561d8e2a 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -680,6 +680,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
 			opt_key == "fill_pattern" ) {
 			ret = static_cast<int>(config.option<ConfigOptionEnum<InfillPattern>>(opt_key)->value);
 		}
+		else if (opt_key.compare("ironing_type") == 0 ) {
+			ret = static_cast<int>(config.option<ConfigOptionEnum<IroningType>>(opt_key)->value);
+		}
 		else if (opt_key.compare("gcode_flavor") == 0 ) {
 			ret = static_cast<int>(config.option<ConfigOptionEnum<GCodeFlavor>>(opt_key)->value);
 		}
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 3731f03de..a1f83141a 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -405,8 +405,9 @@ const std::vector<std::string>& Preset::print_options()
         "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
         "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
         "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
-        "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed",
-        "max_volumetric_speed",
+        "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", 
+    	"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
+        "max_print_speed", "max_volumetric_speed",
 #ifdef HAS_PRESSURE_EQUALIZER
         "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
 #endif /* HAS_PRESSURE_EQUALIZER */
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 0c68979f1..2caa7baaf 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -1162,6 +1162,12 @@ void TabPrint::build()
         optgroup->append_single_option_line("top_fill_pattern");
         optgroup->append_single_option_line("bottom_fill_pattern");
 
+        optgroup = page->new_optgroup(_(L("Ironing")));
+        optgroup->append_single_option_line("ironing");
+        optgroup->append_single_option_line("ironing_type");
+        optgroup->append_single_option_line("ironing_flowrate");
+        optgroup->append_single_option_line("ironing_spacing");
+
         optgroup = page->new_optgroup(_(L("Reducing printing time")));
         optgroup->append_single_option_line("infill_every_layers");
         optgroup->append_single_option_line("infill_only_where_needed");
@@ -1222,6 +1228,7 @@ void TabPrint::build()
         optgroup->append_single_option_line("support_material_interface_speed");
         optgroup->append_single_option_line("bridge_speed");
         optgroup->append_single_option_line("gap_fill_speed");
+        optgroup->append_single_option_line("ironing_speed");
 
         optgroup = page->new_optgroup(_(L("Speed for non-print moves")));
         optgroup->append_single_option_line("travel_speed");

From 45147d887b28679a9c69a5867c5409ebf39c435a Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 14 Apr 2020 17:54:15 +0200
Subject: [PATCH 25/55] Implemented cursor movement inside SearchComboPopup

+ Added checkboxes for editing of the option's name (include "Group"/"Category" or not)
+ some code refactoring
---
 src/slic3r/GUI/GLCanvas3D.cpp   | 19 +++++--
 src/slic3r/GUI/ImGuiWrapper.cpp | 21 ++++++-
 src/slic3r/GUI/ImGuiWrapper.hpp |  3 +-
 src/slic3r/GUI/Plater.cpp       |  4 +-
 src/slic3r/GUI/Plater.hpp       |  2 +-
 src/slic3r/GUI/Search.cpp       | 98 ++++++++++++++++++++++++++++++++-
 src/slic3r/GUI/Search.hpp       | 55 ++++++------------
 7 files changed, 152 insertions(+), 50 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 9fa1d8066..611944fa3 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4480,28 +4480,39 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
 
     size_t selected = size_t(-1);
     bool edited = false;
+    bool check_changed = false;
     float em = static_cast<float>(wxGetApp().em_unit());
 #if ENABLE_RETINA_GL
 	em *= m_retina_helper->get_scale_factor();
 #endif
 
-    std::string& search_line = wxGetApp().sidebar().get_search_line();
+    Sidebar& sidebar = wxGetApp().sidebar();
+
+    std::string& search_line = sidebar.get_search_line();
     char *s = new char[255];
     strcpy(s, search_line.empty() ? _u8L("Type here to search").c_str() : search_line.c_str());
 
-    imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, selected, edited);
+    imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, 
+                       sidebar.get_searcher().category, sidebar.get_searcher().group, 
+                       selected, edited, check_changed);
 
     search_line = s;
     delete [] s;
 
     if (edited)
-        wxGetApp().sidebar().search_and_apply_tab_search_lines();
+        sidebar.search_and_apply_tab_search_lines();
+
+    if (check_changed) {
+        if (search_line == _u8L("Type here to search"))
+            search_line.clear();
+        sidebar.search_and_apply_tab_search_lines(true);
+    }
 
     if (selected != size_t(-1))
     {
         // selected == 9999 means that Esc kye was pressed
         if (selected != 9999)
-            wxGetApp().sidebar().jump_to_option(selected);
+            sidebar.jump_to_option(selected);
         action_taken = true;
     }
 
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 2faf14952..a9b51874a 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -24,6 +24,7 @@
 #include "libslic3r/Utils.hpp"
 #include "3DScene.hpp"
 #include "GUI.hpp"
+#include "I18N.hpp"
 
 namespace Slic3r {
 namespace GUI {
@@ -538,7 +539,8 @@ static bool selectable(const char* label, bool selected, ImGuiSelectableFlags fl
     return pressed;
 }
 
-void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char**), char* search_str, size_t& selected, bool& edited)
+void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char**), char* search_str,
+                               bool& category, bool& group, size_t& selected, bool& edited, bool& check_changed)
 {
     // ImGui::ListBoxHeader("", size);
     {   
@@ -604,6 +606,23 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
     }
 
     ImGui::ListBoxFooter();
+
+    // add checkboxes for show/hide Categories and Groups
+    text(_L("Use for search")+":");
+    ImGui::SameLine();
+    bool cat = category;
+    checkbox(_L("Category"), cat);
+    if (ImGui::IsItemClicked()) {
+        category = !category;
+        check_changed = true;
+    }
+    ImGui::SameLine();
+    bool gr = group;
+    checkbox(_L("Group"), gr);
+    if (ImGui::IsItemClicked()) {
+        group = !group;
+        check_changed = true;
+    }
 }
 
 void ImGuiWrapper::disabled_begin(bool disabled)
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index 9001f6b5a..781d2a25f 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -74,7 +74,8 @@ public:
     bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
     bool combo(const wxString& label, const std::vector<std::string>& options, int& selection);   // Use -1 to not mark any option as selected
     bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
-    void search_list(const ImVec2& size, bool (*items_getter)(int, const char**), char* search_str, size_t& selected, bool& edited);
+    void search_list(const ImVec2& size, bool (*items_getter)(int, const char**), char* search_str, 
+                     bool& category, bool& group, size_t& selected, bool& edited, bool& check_changed);
 
     void disabled_begin(bool disabled);
     void disabled_end();
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 0dbd11827..6cea1a7ef 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1091,9 +1091,9 @@ void Sidebar::msw_rescale()
     p->scrolled->Layout();
 }
 
-void Sidebar::search_and_apply_tab_search_lines()
+void Sidebar::search_and_apply_tab_search_lines(bool force/* = false*/)
 {
-    if (p->searcher.search(p->search_line))
+    if (p->searcher.search(p->search_line, force))
         apply_search_line_on_tabs();
 }
 
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 52a24ef36..ae7b241d9 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -105,7 +105,7 @@ public:
     void update_mode_sizer() const;
     void update_reslice_btn_tooltip() const;
     void msw_rescale();
-    void search_and_apply_tab_search_lines();
+    void search_and_apply_tab_search_lines(bool force = false);
     void jump_to_option(size_t selected);
 
     ObjectManipulation*     obj_manipul();
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 49f74b340..b55f4fbab 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -186,11 +186,22 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
 
     bool full_list = search.empty();
 
+    auto get_label = [this](const Option& opt)
+    {
+        wxString label;
+        if (category)
+            label += opt.category_local + " > ";
+        if (group)
+            label += opt.group_local + " > ";
+        label += opt.label_local;
+        return label;
+    };
+
     for (size_t i=0; i < options.size(); i++)
     {
         const Option &opt = options[i];
         if (full_list) {
-            wxString label = opt.category_local + " > " + opt.group_local + " > " + opt.label_local;
+            wxString label = get_label(opt);
             found.emplace_back(FoundOption{ label, label, i, 0 });
             continue;
         }
@@ -200,7 +211,8 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
         FMFlag fuzzy_match_flag = opt.fuzzy_match(search, score);
         if (fuzzy_match_flag != fmUndef)
         {
-            wxString label = opt.category_local + " > " + opt.group_local  + " > " + opt.label_local;
+            wxString label = get_label(opt); //opt.category_local + " > " + opt.group_local  + " > " + opt.label_local;
+
             if (     fuzzy_match_flag == fmLabel   ) label += "(" + opt.label    + ")";
             else if (fuzzy_match_flag == fmGroup   ) label += "(" + opt.group    + ")";
             else if (fuzzy_match_flag == fmCategory) label += "(" + opt.category + ")";
@@ -243,6 +255,81 @@ void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group,
 }
 
 
+//------------------------------------------
+//          SearchComboPopup
+//------------------------------------------
+
+
+void SearchComboPopup::Init()
+{
+    this->Bind(wxEVT_MOTION,    &SearchComboPopup::OnMouseMove,     this);
+    this->Bind(wxEVT_LEFT_UP,   &SearchComboPopup::OnMouseClick,    this);
+    this->Bind(wxEVT_KEY_DOWN,  &SearchComboPopup::OnKeyDown,       this);
+}
+
+bool SearchComboPopup::Create(wxWindow* parent)
+{
+    return wxListBox::Create(parent, 1, wxPoint(0, 0), wxDefaultSize);
+}
+
+void SearchComboPopup::SetStringValue(const wxString& s)
+{
+    int n = wxListBox::FindString(s);
+    if (n >= 0 && n < wxListBox::GetCount())
+        wxListBox::Select(n);
+
+    // save a combo control's string
+    m_input_string = s;
+}
+
+void SearchComboPopup::ProcessSelection(int selection) 
+{
+    wxCommandEvent event(wxEVT_LISTBOX, GetId());
+    event.SetInt(selection);
+    event.SetEventObject(this);
+    ProcessEvent(event);
+
+    Dismiss();
+}
+
+void SearchComboPopup::OnMouseMove(wxMouseEvent& event)
+{
+    wxPoint pt = wxGetMousePosition() - this->GetScreenPosition();
+    int selection = this->HitTest(pt);
+    wxListBox::Select(selection);
+}
+
+void SearchComboPopup::OnMouseClick(wxMouseEvent&)
+{
+    int selection = wxListBox::GetSelection();
+    SetSelection(wxNOT_FOUND);
+    ProcessSelection(selection);
+}
+
+void SearchComboPopup::OnKeyDown(wxKeyEvent& event)
+{
+    int key = event.GetKeyCode();
+
+    // change selected item in the list
+    if (key == WXK_UP || key == WXK_DOWN)
+    {
+        int selection = wxListBox::GetSelection();
+
+        if (key == WXK_UP && selection > 0)
+            selection--;
+        int last_item_id = int(wxListBox::GetCount() - 1);
+        if (key == WXK_DOWN && selection < int(wxListBox::GetCount() - 1))
+            selection++;
+
+        wxListBox::Select(selection);
+    }
+    // send wxEVT_LISTBOX event if "Enter" was pushed
+    else if (key == WXK_NUMPAD_ENTER || key == WXK_RETURN)
+        ProcessSelection(wxListBox::GetSelection());
+    else
+        event.Skip(); // !Needed to have EVT_CHAR generated as well
+}
+
 //------------------------------------------
 //          SearchCtrl
 //------------------------------------------
@@ -266,7 +353,12 @@ SearchCtrl::SearchCtrl(wxWindow* parent) :
     this->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
     this->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
     this->Bind(wxEVT_COMBOBOX_DROPDOWN,    &SearchCtrl::PopupList, this);
-
+/*
+    this->Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent&e)
+    {
+        
+    });
+*/
     this->GetTextCtrl()->Bind(wxEVT_LEFT_UP,    &SearchCtrl::OnLeftUpInTextCtrl, this);
     popupListBox->Bind(wxEVT_LISTBOX,           &SearchCtrl::OnSelect,           this);
 }
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 8488781a3..83df4136c 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -97,6 +97,9 @@ class OptionsSearcher
     size_t found_size() const { return found.size(); }
 
 public:
+    bool                category{ false };
+    bool                group{ true };
+
     void init(std::vector<InputInfo> input_values);
     bool search(const std::string& search, bool force = false);
 
@@ -115,54 +118,30 @@ class SearchComboPopup : public wxListBox, public wxComboPopup
 {
 public:
     // Initialize member variables
-    virtual void Init()
-    {
-        this->Bind(wxEVT_MOTION, &SearchComboPopup::OnMouseMove, this);
-        this->Bind(wxEVT_LEFT_UP, &SearchComboPopup::OnMouseClick, this);
-    }
+    void Init();
 
     // Create popup control
-    virtual bool Create(wxWindow* parent)
-    {
-        return wxListBox::Create(parent, 1, wxPoint(0, 0), wxDefaultSize);
-    }
+    virtual bool Create(wxWindow* parent);
     // Return pointer to the created control
     virtual wxWindow* GetControl() { return this; }
-    // Translate string into a list selection
-    virtual void SetStringValue(const wxString& s)
-    {
-        int n = wxListBox::FindString(s);
-        if (n >= 0 && n < wxListBox::GetCount())
-            wxListBox::Select(n);
 
-        // save a combo control's string
-        m_input_string = s;
-    }
+    // Translate string into a list selection
+    virtual void SetStringValue(const wxString& s);
     // Get list selection as a string
-    virtual wxString GetStringValue() const
-    {
+    virtual wxString GetStringValue() const {
         // we shouldn't change a combo control's string
         return m_input_string;
     }
-    // Do mouse hot-tracking (which is typical in list popups)
-    void OnMouseMove(wxMouseEvent& event)
-    {
-        wxPoint pt = wxGetMousePosition() - this->GetScreenPosition();
-        int selection = this->HitTest(pt);
-        wxListBox::Select(selection);
-    }
-    // On mouse left up, set the value and close the popup
-    void OnMouseClick(wxMouseEvent& WXUNUSED(event))
-    {
-        int selection = wxListBox::GetSelection();
-        SetSelection(wxNOT_FOUND);
-        wxCommandEvent event(wxEVT_LISTBOX, GetId());
-        event.SetInt(selection);
-        event.SetEventObject(this);
-        ProcessEvent(event);
 
-        Dismiss();
-    }
+    void ProcessSelection(int selection);
+
+    // Do mouse hot-tracking (which is typical in list popups)
+    void OnMouseMove(wxMouseEvent& event);
+    // On mouse left up, set the value and close the popup
+    void OnMouseClick(wxMouseEvent& WXUNUSED(event));
+    // process Up/Down arrows and Enter press
+    void OnKeyDown(wxKeyEvent& event);
+
 protected:
     wxString m_input_string;
 };

From b69dfd63ca3179de4c3d53326c97b7edf5caca4d Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 16 Apr 2020 09:59:12 +0200
Subject: [PATCH 26/55] Completed a search list cleaning (deleted unused
 options).

+ Implemented BlinkingBitmap
+ Options, that doesn't have related controls, are highlighted near the widgets.
---
 src/slic3r/GUI/Field.cpp        |  3 +-
 src/slic3r/GUI/Field.hpp        | 19 ++-----
 src/slic3r/GUI/OptionsGroup.cpp |  2 +-
 src/slic3r/GUI/Search.cpp       | 76 +++++++++++++++++++-------
 src/slic3r/GUI/Search.hpp       | 10 ++--
 src/slic3r/GUI/Tab.cpp          | 95 +++++++++++++++++++++++++--------
 src/slic3r/GUI/Tab.hpp          | 23 +++++---
 src/slic3r/GUI/wxExtensions.cpp | 35 ++++++++++++
 src/slic3r/GUI/wxExtensions.hpp | 24 +++++++++
 9 files changed, 219 insertions(+), 68 deletions(-)

diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index dfe561caf..f78714fcb 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -57,8 +57,7 @@ void Field::PostInitialize()
     m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_initial_value(); }));
 	m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_sys_value(); }));
 
-	m_attention_bmp		= ScalableBitmap(m_parent, "attention");
-	m_find_image		= new wxStaticBitmap(m_parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, m_attention_bmp.bmp().GetSize());
+	m_blinking_bmp		= new BlinkingBitmap(m_parent);
 
 	switch (m_opt.type)
 	{
diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp
index d716be25c..f41e3c7b0 100644
--- a/src/slic3r/GUI/Field.hpp
+++ b/src/slic3r/GUI/Field.hpp
@@ -191,20 +191,6 @@ public:
 		return false;
 	}
 
-	void	invalidate_attention_bmp() const {
-        m_find_image->SetBitmap(wxNullBitmap);
-		m_find_image->Show();
-    }
-
-	void	activate_attention_bmp() const {
-		m_find_image->SetBitmap(m_attention_bmp.bmp());
-	}
-
-	void	blink_attention_bmp() const {
-		bool is_shown = m_find_image->IsShown();
-		m_find_image->Show(!is_shown);
-	}
-
 	bool	set_label_colour_force(const wxColour *clr) {
 		if (m_Label == nullptr) return false;
 		m_Label->SetForegroundColour(*clr);
@@ -244,6 +230,8 @@ public:
 	static int def_width_wider()	;
 	static int def_width_thinner()	;
 
+	BlinkingBitmap*			blinking_bitmap() const { return m_blinking_bmp;}
+
 protected:
 	RevertButton*			m_Undo_btn = nullptr;
 	// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
@@ -254,8 +242,7 @@ protected:
     const ScalableBitmap*   m_undo_to_sys_bitmap = nullptr;
 	const wxString*		    m_undo_to_sys_tooltip = nullptr;
 
-	ScalableBitmap			m_attention_bmp;
-	wxStaticBitmap*			m_find_image{ nullptr };
+	BlinkingBitmap*			m_blinking_bmp{ nullptr };
 
 	wxStaticText*		m_Label = nullptr;
 	// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index bafcac3a4..22e259244 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -108,9 +108,9 @@ void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& fiel
 		return;
 	}
 
+    sizer->Add(field->m_blinking_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 2);
 	sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
 	sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
-    sizer->Add(field->m_find_image, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 2);
 }
 
 void OptionsGroup::append_line(const Line& line, wxStaticText**	full_Label/* = nullptr*/) {
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index b55f4fbab..f5b36cf89 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -92,16 +92,35 @@ void FoundOption::get_marked_label(const char** out_text) const
 }
 
 template<class T>
-void change_opt_key(std::string& opt_key, DynamicPrintConfig* config)
+//void change_opt_key(std::string& opt_key, DynamicPrintConfig* config)
+void change_opt_key(std::string& opt_key, DynamicPrintConfig* config, int& cnt)
 {
     T* opt_cur = static_cast<T*>(config->option(opt_key));
+    cnt = opt_cur->values.size();
+    return;
+
     if (opt_cur->values.size() > 0)
         opt_key += "#" + std::to_string(0);
 }
 
 void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
 {
-    std::vector<std::string> non_added_options {"printer_technology", "thumbnails" };
+    auto emplace = [this, type](const std::string opt_key, const wxString& label)
+    {
+        const GroupAndCategory& gc = groups_and_categories[opt_key];
+        if (gc.group.IsEmpty() || gc.category.IsEmpty())
+            return;
+
+        wxString suffix;
+        if (gc.category == "Machine limits")
+            suffix = opt_key.back()=='1' ? L("Stealth") : L("Normal");
+
+        if (!label.IsEmpty())
+            options.emplace_back(Option{ opt_key, type,
+                                        label+ " " + suffix, _(label)+ " " + _(suffix),
+                                        gc.group, _(gc.group),
+                                        gc.category, _(gc.category) });
+    };
 
     for (std::string opt_key : config->keys())
     {
@@ -109,27 +128,37 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty
         if (opt.mode > mode)
             continue;
 
-        if (type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER)
+        int cnt = 0;
+
+        if ( (type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER) && opt_key != "bed_shape")
             switch (config->option(opt_key)->type())
             {
-            case coInts:	change_opt_key<ConfigOptionInts		>(opt_key, config);	break;
-            case coBools:	change_opt_key<ConfigOptionBools	>(opt_key, config);	break;
-            case coFloats:	change_opt_key<ConfigOptionFloats	>(opt_key, config);	break;
-            case coStrings:	change_opt_key<ConfigOptionStrings	>(opt_key, config);	break;
-            case coPercents:change_opt_key<ConfigOptionPercents	>(opt_key, config);	break;
-            case coPoints:	change_opt_key<ConfigOptionPoints	>(opt_key, config);	break;
+            case coInts:	change_opt_key<ConfigOptionInts		>(opt_key, config, cnt);	break;
+            case coBools:	change_opt_key<ConfigOptionBools	>(opt_key, config, cnt);	break;
+            case coFloats:	change_opt_key<ConfigOptionFloats	>(opt_key, config, cnt);	break;
+            case coStrings:	change_opt_key<ConfigOptionStrings	>(opt_key, config, cnt);	break;
+            case coPercents:change_opt_key<ConfigOptionPercents	>(opt_key, config, cnt);	break;
+            case coPoints:	change_opt_key<ConfigOptionPoints	>(opt_key, config, cnt);	break;
             default:		break;
             }
 
         wxString label = opt.full_label.empty() ? opt.label : opt.full_label;
 
-        const GroupAndCategory& gc = groups_and_categories[opt_key];
+        if (cnt == 0)
+            emplace(opt_key, label);
+        else
+            for (int i = 0; i < cnt; ++i)
+                emplace(opt_key + "#" + std::to_string(i), label);
+
+        /*const GroupAndCategory& gc = groups_and_categories[opt_key];
+        if (gc.group.IsEmpty() || gc.category.IsEmpty())
+            continue;
 
         if (!label.IsEmpty())
             options.emplace_back(Option{opt_key, type,
                                         label, _(label),
                                         gc.group, _(gc.group),
-                                        gc.category, _(gc.category) });
+                                        gc.category, _(gc.category) });*/
     }
 }
 
@@ -243,6 +272,22 @@ void OptionsSearcher::init(std::vector<InputInfo> input_values)
     search(search_line, true);
 }
 
+void OptionsSearcher::apply(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
+{
+    if (options.empty())
+        return;
+
+    options.erase(std::remove_if(options.begin(), options.end(), [type](Option opt) {
+            return opt.type == type;
+        }), options.end());
+
+    append_options(config, type, mode);
+
+    sort_options();
+
+    search(search_line, true);
+}
+
 const Option& OptionsSearcher::get_option(size_t pos_in_filter) const
 {
     assert(pos_in_filter != size_t(-1) && found[pos_in_filter].option_idx != size_t(-1));
@@ -353,12 +398,7 @@ SearchCtrl::SearchCtrl(wxWindow* parent) :
     this->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
     this->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
     this->Bind(wxEVT_COMBOBOX_DROPDOWN,    &SearchCtrl::PopupList, this);
-/*
-    this->Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent&e)
-    {
-        
-    });
-*/
+
     this->GetTextCtrl()->Bind(wxEVT_LEFT_UP,    &SearchCtrl::OnLeftUpInTextCtrl, this);
     popupListBox->Bind(wxEVT_LISTBOX,           &SearchCtrl::OnSelect,           this);
 }
@@ -421,7 +461,7 @@ void SearchCtrl::OnSelect(wxCommandEvent& event)
 
 void SearchCtrl::update_list(const std::vector<FoundOption>& filters)
 {
-    if (popupListBox->GetCount() == filters.size() &&
+    if (!filters.empty() && popupListBox->GetCount() == filters.size() &&
         popupListBox->GetString(0) == filters[0].label &&
         popupListBox->GetString(popupListBox->GetCount()-1) == filters[filters.size()-1].label)
         return;
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 83df4136c..3f24fbd3a 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -76,11 +76,11 @@ struct FoundOption {
 
 class OptionsSearcher
 {
-    std::string         search_line;
+    std::string                             search_line;
     std::map<std::string, GroupAndCategory> groups_and_categories;
 
-    std::vector<Option>         options {};
-    std::vector<FoundOption>    found {};
+    std::vector<Option>                     options {};
+    std::vector<FoundOption>                found {};
 
     void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
 
@@ -101,6 +101,9 @@ public:
     bool                group{ true };
 
     void init(std::vector<InputInfo> input_values);
+    void apply(DynamicPrintConfig *config,
+               Preset::Type        type,
+               ConfigOptionMode    mode);
     bool search(const std::string& search, bool force = false);
 
     void add_key(const std::string& opt_key, const wxString& group, const wxString& category);
@@ -111,6 +114,7 @@ public:
     const Option& get_option(size_t pos_in_filter) const;
 
     const std::vector<FoundOption>& found_options() { return found; }
+    const GroupAndCategory&         get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; }
 };
 
 
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index e63f7dff7..0f830b4d6 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -39,26 +39,41 @@ namespace GUI {
 wxDEFINE_EVENT(EVT_TAB_VALUE_CHANGED, wxCommandEvent);
 wxDEFINE_EVENT(EVT_TAB_PRESETS_CHANGED, SimpleEvent);
 
-void Tab::Highlighter::init(Field* f)
+void Tab::Highlighter::set_timer_owner(wxEvtHandler* owner, int timerid/* = wxID_ANY*/)
 {
-    field = f;
-    field->activate_attention_bmp();
+    timer.SetOwner(owner, timerid);
+}
+
+void Tab::Highlighter::init(BlinkingBitmap* bmp)
+{
+    if (timer.IsRunning())
+        invalidate();
+    if (!bmp)
+        return;
+
+    timer.Start(100, false);
+
+    bbmp = bmp;
+    bbmp->activate();
 }
 
 void Tab::Highlighter::invalidate()
 {
-    field->invalidate_attention_bmp();
-    field = nullptr;
+    timer.Stop();
+
+    bbmp->invalidate();
+    bbmp = nullptr;
     blink_counter = 0;
 }
 
-bool Tab::Highlighter::blink()
+void Tab::Highlighter::blink()
 {
-    field->blink_attention_bmp();
+    if (!bbmp)
+        return;
+
+    bbmp->blink();
     if ((++blink_counter) == 29)
         invalidate();
-
-    return blink_counter != 0;
 }
 
 Tab::Tab(wxNotebook* parent, const wxString& title, Preset::Type type) :
@@ -92,11 +107,10 @@ Tab::Tab(wxNotebook* parent, const wxString& title, Preset::Type type) :
         evt.Skip();
     }));
 
-    this->m_highlighting_timer.SetOwner(this, 0);
+    m_highlighter.set_timer_owner(this, 0);
     this->Bind(wxEVT_TIMER, [this](wxTimerEvent&)
     {
-        if (!m_highlighter.blink())
-            m_highlighting_timer.Stop();
+        m_highlighter.blink();
     });
 }
 
@@ -1021,19 +1035,28 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
     // focused selected field
     if (field) {
         field->getWindow()->SetFocus();
-        if (m_highlighting_timer.IsRunning()) {
-            m_highlighting_timer.Stop();
-            m_highlighter.invalidate();
+        m_highlighter.init(field->blinking_bitmap());
+    }
+    else if (category == "Single extruder MM setup")
+    {
+        // When we show and hide "Single extruder MM setup" page, 
+        // related options are still in the search list
+        // So, let's hightlighte a "single_extruder_multi_material" option, 
+        // as a "way" to show hidden page again
+        field = get_field("single_extruder_multi_material");
+        if (field) {
+            field->getWindow()->SetFocus();
+            m_highlighter.init(field->blinking_bitmap());
         }
-        m_highlighting_timer.Start(100, false);
-        m_highlighter.init(field);
     }
     else
-    {
-        // "bed_shape", "bed_custom_texture", "bed_custom_model"
+        m_highlighter.init(m_blinking_ikons[opt_key]);
 
+}
 
-    }
+void Tab::apply_searcher()
+{
+    wxGetApp().sidebar().get_searcher().apply(m_config, m_type, m_mode);
 }
 
 
@@ -2533,6 +2556,9 @@ void TabPrinter::build_unregular_pages()
 
     // Reload preset pages with current configuration values
     reload_config();
+
+    // apply searcher with current configuration
+    apply_searcher();
 }
 
 // this gets executed after preset is loaded and before GUI fields are updated
@@ -3321,7 +3347,10 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
     add_scaled_button(parent, &deps.btn, "printer_white", from_u8((boost::format(" %s %s") % _utf8(L("Set")) % std::string(dots.ToUTF8())).str()), wxBU_LEFT | wxBU_EXACTFIT);
     deps.btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
 
+    BlinkingBitmap* bbmp = new BlinkingBitmap(parent);
+
     auto sizer = new wxBoxSizer(wxHORIZONTAL);
+    sizer->Add(bbmp, 0, wxALIGN_CENTER_VERTICAL);
     sizer->Add((deps.checkbox), 0, wxALIGN_CENTER_VERTICAL);
     sizer->Add((deps.btn), 0, wxALIGN_CENTER_VERTICAL);
 
@@ -3381,6 +3410,12 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
             this->update_changed_ui();
         }
     }));
+
+    // fill m_blinking_ikons map with options
+    {
+        m_blinking_ikons[deps.key_list] = bbmp;
+    }
+
     return sizer;
 }
 
@@ -3391,8 +3426,11 @@ wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent)
     add_scaled_button(parent, &btn, "printer_white", " " + _(L("Set")) + " " + dots, wxBU_LEFT | wxBU_EXACTFIT);
     btn->SetFont(wxGetApp().normal_font());
 
+    BlinkingBitmap* bbmp = new BlinkingBitmap(parent);
+
     auto sizer = new wxBoxSizer(wxHORIZONTAL);
-    sizer->Add(btn);
+    sizer->Add(bbmp, 0, wxALIGN_CENTER_VERTICAL);
+    sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL);
 
     btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
         {
@@ -3414,6 +3452,21 @@ wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent)
             }
         }));
 
+    // may be it is not a best place, but 
+    // add information about Category/Grope for "bed_custom_texture" and "bed_custom_model" as a copy from "bed_shape" option
+    {
+        Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
+        const Search::GroupAndCategory& gc = searcher.get_group_and_category("bed_shape");
+        searcher.add_key("bed_custom_texture", gc.group, gc.category);
+        searcher.add_key("bed_custom_model", gc.group, gc.category);
+    }
+
+    // fill m_blinking_ikons map with options
+    {
+        for (const std::string opt : {"bed_shape", "bed_custom_texture", "bed_custom_model"})
+            m_blinking_ikons[opt] = bbmp;
+    }
+
     return sizer;
 }
 
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 3bce5de6c..316012016 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -223,16 +223,20 @@ protected:
     bool                m_completed { false };
     ConfigOptionMode    m_mode = comExpert; // to correct first Tab update_visibility() set mode to Expert
 
-	wxTimer             m_highlighting_timer;
 	struct Highlighter
 	{
-		void init(Field* f);
-		void invalidate();
-		bool blink();
+		void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY);
+		void init(BlinkingBitmap* bmp);
+		void blink();
+
 	private:
-	    Field*	field {nullptr};
-		int		blink_counter {0};
-	} m_highlighter;
+		void invalidate();
+
+		BlinkingBitmap*	bbmp {nullptr};
+		int				blink_counter {0};
+	    wxTimer         timer;
+	} 
+    m_highlighter;
 
 public:
 	PresetBundle*		m_preset_bundle;
@@ -246,6 +250,10 @@ public:
     // Used for options which don't have corresponded field
 	std::map<std::string, wxStaticText*>	m_colored_Labels;
 
+	// map of option name -> BlinkingBitmap (blinking ikon, associated with option) 
+    // Used for options which don't have corresponded field
+	std::map<std::string, BlinkingBitmap*>	m_blinking_ikons;
+
     // Counter for the updating (because of an update() function can have a recursive behavior):
     // 1. increase value from the very beginning of an update() function
     // 2. decrease value at the end of an update() function
@@ -328,6 +336,7 @@ public:
 
     void            update_wiping_button_visibility();
 	void			activate_option(const std::string& opt_key, const wxString& category);
+    void			apply_searcher();
 
 protected:
 	void			create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget);
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index c9f1de168..2402347f8 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -942,5 +942,40 @@ void ScalableButton::msw_rescale()
 }
 
 
+// ----------------------------------------------------------------------------
+// BlinkingBitmap
+// ----------------------------------------------------------------------------
+
+BlinkingBitmap::BlinkingBitmap(wxWindow* parent, const std::string& icon_name) :
+    wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(int(1.6 * Slic3r::GUI::wxGetApp().em_unit()), -1))
+{
+    bmp = ScalableBitmap(parent, icon_name);
+}
+
+void BlinkingBitmap::msw_rescale()
+{
+    bmp.msw_rescale();
+    this->SetSize(bmp.GetBmpSize());
+    this->SetMinSize(bmp.GetBmpSize());
+}
+
+void BlinkingBitmap::invalidate()
+{
+    this->SetBitmap(wxNullBitmap);
+}
+
+void BlinkingBitmap::activate()
+{
+    this->SetBitmap(bmp.bmp());
+    show = true;
+}
+
+void BlinkingBitmap::blink()
+{
+    show = !show;
+    this->SetBitmap(show ? bmp.bmp() : wxNullBitmap);
+}
+
+
 
 
diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index 38d726ad3..569257e1b 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -8,6 +8,7 @@
 #include <wx/sizer.h>
 #include <wx/menu.h>
 #include <wx/bmpcbox.h>
+#include <wx/statbmp.h>
 
 #include <vector>
 #include <functional>
@@ -355,5 +356,28 @@ private:
 };
 
 
+// ----------------------------------------------------------------------------
+// BlinkingBitmap
+// ----------------------------------------------------------------------------
+
+class BlinkingBitmap : public wxStaticBitmap
+{
+public:
+    BlinkingBitmap() {};
+    BlinkingBitmap(wxWindow* parent, const std::string& icon_name = "redo_toolbar");
+
+    ~BlinkingBitmap() {}
+
+    void    msw_rescale();
+    void    invalidate();
+    void    activate();
+    void    blink();
+
+private:
+    ScalableBitmap  bmp;
+    bool            show {false};
+};
+
+
 
 #endif // slic3r_GUI_wxExtensions_hpp_

From 6a8d0c5d846b90ce65c08de1fae8060ab2e9d7c4 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 16 Apr 2020 15:49:40 +0200
Subject: [PATCH 27/55] Search: Experiment button

---
 src/slic3r/GUI/Search.cpp | 200 +++++++++++++++++++++++++++++++++++++-
 src/slic3r/GUI/Search.hpp |  41 ++++++++
 src/slic3r/GUI/Tab.cpp    |   4 +
 src/slic3r/GUI/Tab.hpp    |   2 +-
 4 files changed, 245 insertions(+), 2 deletions(-)

diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index f5b36cf89..ecc002476 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -362,7 +362,6 @@ void SearchComboPopup::OnKeyDown(wxKeyEvent& event)
 
         if (key == WXK_UP && selection > 0)
             selection--;
-        int last_item_id = int(wxListBox::GetCount() - 1);
         if (key == WXK_DOWN && selection < int(wxListBox::GetCount() - 1))
             selection++;
 
@@ -479,6 +478,205 @@ void SearchCtrl::OnLeftUpInTextCtrl(wxEvent &event)
     event.Skip();
 }
 
+
+//------------------------------------------
+//          PopupSearchList
+//------------------------------------------
+
+PopupSearchList::PopupSearchList(wxWindow* parent) :
+    wxPopupTransientWindow(parent, /*wxSTAY_ON_TOP*/wxWANTS_CHARS | wxBORDER_NONE)
+{
+    panel = new wxPanel(this, wxID_ANY);
+
+    text = new wxTextCtrl(panel, 1);
+    list = new wxListBox(panel, 2, wxDefaultPosition, wxSize(GUI::wxGetApp().em_unit() * 40, -1));
+    check = new wxCheckBox(panel, 3, "Group");
+
+    wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
+
+    text->Bind(wxEVT_MOUSE_CAPTURE_CHANGED, [](wxEvent& e) {
+        int i = 0; });
+
+//    text->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e) {
+    text->Bind(wxEVT_LEFT_UP, [this](wxEvent& e) {
+        text->SetValue("mrrrrrty");
+    });
+
+    text->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt)
+    {
+        wxPoint pt = wxGetMousePosition() - text->GetScreenPosition();
+        long pos;
+        text->HitTest(pt, &pos);
+
+        if (pos == wxTE_HT_UNKNOWN)
+            return;
+
+        list->SetSelection(wxNOT_FOUND);
+        text->SetSelection(0, pos);
+    });
+
+    text->Bind(wxEVT_TEXT, [this](wxCommandEvent& e)
+    {
+        text->SetSelection(0, 3);
+    });
+
+    this->Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& event) {
+        int key = event.GetKeyCode();
+
+        // change selected item in the list
+        if (key == WXK_UP || key == WXK_DOWN)
+        {
+            int selection = list->GetSelection();
+
+            if (key == WXK_UP && selection > 0)
+                selection--;
+            if (key == WXK_DOWN && selection < int(list->GetCount() - 1))
+                selection++;
+
+            list->Select(selection);
+        }
+        else
+            event.Skip(); // !Needed to have EVT_CHAR generated as well
+    });
+
+    this->Bind(wxEVT_CHAR, [this](wxKeyEvent& e) {
+        int key = e.GetKeyCode();
+        wxChar symbol = e.GetUnicodeKey();
+        search_str += symbol;
+
+        text->SetValue(search_str);
+    });
+
+
+    list->Append("One");
+    list->Append("Two");
+    list->Append("Three");
+
+    list->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt)
+        {
+            int selection = list->GetSelection();
+        });
+
+    list->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& evt)
+    {
+        int selection = list->GetSelection();
+        list->SetSelection(wxNOT_FOUND);
+
+        wxCommandEvent event(wxEVT_LISTBOX, list->GetId());
+        event.SetInt(selection);
+        event.SetEventObject(this);
+        ProcessEvent(event);
+
+        Dismiss();
+    });
+
+    list->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt)
+    {
+        wxPoint pt = wxGetMousePosition() - list->GetScreenPosition();
+        int selection = list->HitTest(pt);
+        list->Select(selection);
+    });
+
+    list->Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& event) {
+        int key = event.GetKeyCode();
+
+        // change selected item in the list
+        if (key == WXK_UP || key == WXK_DOWN)
+        {
+            int selection = list->GetSelection();
+
+            if (key == WXK_UP && selection > 0)
+                selection--;
+            if (key == WXK_DOWN && selection < int(list->GetCount() - 1))
+                selection++;
+
+            list->Select(selection);
+        }
+        // send wxEVT_LISTBOX event if "Enter" was pushed
+        else if (key == WXK_NUMPAD_ENTER || key == WXK_RETURN)
+        {
+            int selection = list->GetSelection();
+
+            wxCommandEvent event(wxEVT_LISTBOX, list->GetId());
+            event.SetInt(selection);
+            event.SetEventObject(this);
+            ProcessEvent(event);
+
+            Dismiss();
+        }
+        else
+            event.Skip(); // !Needed to have EVT_CHAR generated as well
+    });
+
+    topSizer->Add(text, 0, wxEXPAND | wxALL, 2);
+    topSizer->Add(list, 0, wxEXPAND | wxALL, 2);
+    topSizer->Add(check, 0, wxEXPAND | wxALL, 2);
+
+    panel->SetSizer(topSizer);
+
+    topSizer->Fit(panel);
+    SetClientSize(panel->GetSize());
+}
+
+void PopupSearchList::Popup(wxWindow* WXUNUSED(focus))
+{
+    wxPopupTransientWindow::Popup();
+}
+
+void PopupSearchList::OnDismiss()
+{
+    wxPopupTransientWindow::OnDismiss();
+}
+
+bool PopupSearchList::ProcessLeftDown(wxMouseEvent& event)
+{
+    return wxPopupTransientWindow::ProcessLeftDown(event);
+}
+bool PopupSearchList::Show(bool show)
+{
+    return wxPopupTransientWindow::Show(show);
+}
+
+void PopupSearchList::OnSize(wxSizeEvent& event)
+{
+    event.Skip();
+}
+
+void PopupSearchList::OnSetFocus(wxFocusEvent& event)
+{
+    event.Skip();
+}
+
+void PopupSearchList::OnKillFocus(wxFocusEvent& event)
+{
+    event.Skip();
+}
+
+
+//------------------------------------------
+//          SearchCtrl
+//------------------------------------------
+
+SearchButton::SearchButton(wxWindow* parent) :
+    ScalableButton(parent, wxID_ANY, "search")
+{
+    popup_win = new PopupSearchList(parent);
+    this->Bind(wxEVT_BUTTON, &SearchButton::PopupSearch, this);
+}
+    
+void SearchButton::PopupSearch(wxCommandEvent& e)
+{
+//    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
+    wxPoint pos = this->ClientToScreen(wxPoint(0, 0));
+    wxSize sz = wxSize(GUI::wxGetApp().em_unit()*40, -1);
+    pos.x -= sz.GetWidth();
+    pos.y += this->GetSize().y;
+    popup_win->Position(pos, sz);
+    popup_win->Popup();
+    e.Skip();
+}
+
+
 }
 
 }    // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 3f24fbd3a..0d020d8d8 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -174,6 +174,47 @@ public:
     void        update_list(const std::vector<FoundOption>& filters);
 };
 
+
+#include <wx/popupwin.h>
+
+class PopupSearchList : public wxPopupTransientWindow
+{
+    wxString search_str; 
+public:
+    PopupSearchList(wxWindow* parent);
+    ~PopupSearchList() {}
+
+    // wxPopupTransientWindow virtual methods are all overridden to log them
+    void Popup(wxWindow* focus = NULL) wxOVERRIDE;
+    void OnDismiss() wxOVERRIDE;
+    bool ProcessLeftDown(wxMouseEvent& event) wxOVERRIDE;
+    bool Show(bool show = true) wxOVERRIDE;
+
+private:
+    wxWindow* panel;
+
+    wxTextCtrl* text {nullptr};
+    wxListBox*  list{ nullptr };
+    wxCheckBox* check {nullptr};
+
+    void OnSize(wxSizeEvent& event);
+    void OnSetFocus(wxFocusEvent& event);
+    void OnKillFocus(wxFocusEvent& event);
+};
+
+class SearchButton : public ScalableButton
+{
+    PopupSearchList* popup_win{ nullptr };
+
+    void PopupSearch(wxCommandEvent& event);
+public:
+    SearchButton(wxWindow* parent);
+    ~SearchButton() {}
+};
+
+
+
+
 } // Search namespace
 }
 
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 0f830b4d6..1dc25ed2f 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -160,6 +160,8 @@ void Tab::create_preset_tab()
 
     // search combox
     m_search = new Search::SearchCtrl(panel);
+    // search combox
+    m_search_btn = new Search::SearchButton(panel);
 
     auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
@@ -241,6 +243,8 @@ void Tab::create_preset_tab()
     m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
+    m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL);
+    m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
     m_hsizer->Add(m_search, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->AddSpacer(int(16 * scale_factor));
 //    m_hsizer->AddSpacer(int(32 * scale_factor));
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 316012016..3b9717ccb 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -123,6 +123,7 @@ protected:
 	const wxString		m_title;
 	PresetBitmapComboBox*	m_presets_choice;
 	Search::SearchCtrl*	m_search;
+	Search::SearchButton*	m_search_btn;
 	ScalableButton*		m_btn_save_preset;
 	ScalableButton*		m_btn_delete_preset;
 	ScalableButton*		m_btn_hide_incompatible_presets;
@@ -327,7 +328,6 @@ public:
 
 	DynamicPrintConfig*	get_config() { return m_config; }
 	PresetCollection*	get_presets() { return m_presets; }
-//	SearchComboBox*     get_search_cb() { return m_search_cb; }
 	size_t				get_selected_preset_item() { return m_selected_preset_item; }
 
 	void			set_search_line(const std::string& search_line);

From 3b88dc26886fa15a17423ed6123270532aa7be89 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 20 Apr 2020 17:37:03 +0200
Subject: [PATCH 28/55] Search: Implemented cursor movement inside SearchWindow
 on Plater

---
 src/slic3r/GUI/GLCanvas3D.cpp   |   9 ++-
 src/slic3r/GUI/GLCanvas3D.hpp   |   1 +
 src/slic3r/GUI/ImGuiWrapper.cpp | 105 ++++++++++++++++++++++++++++++--
 src/slic3r/GUI/ImGuiWrapper.hpp |   2 +-
 src/slic3r/GUI/Search.hpp       |   3 +-
 5 files changed, 110 insertions(+), 10 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index fff0ae21a..64338ce30 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4497,14 +4497,17 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
     strcpy(s, search_line.empty() ? _u8L("Type here to search").c_str() : search_line.c_str());
 
     imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, 
-                       sidebar.get_searcher().category, sidebar.get_searcher().group, 
+                       sidebar.get_searcher().category, sidebar.get_searcher().group,
+                       m_imgui_search_hovered_pos,
                        selected, edited, check_changed);
 
     search_line = s;
     delete [] s;
 
-    if (edited)
+    if (edited) {
         sidebar.search_and_apply_tab_search_lines();
+        m_imgui_search_hovered_pos = -1;
+    }
 
     if (check_changed) {
         if (search_line == _u8L("Type here to search"))
@@ -5065,7 +5068,7 @@ bool GLCanvas3D::_init_main_toolbar()
                 _deactivate_search_toolbar_item();
         }
     };
-    item.left.action_callback   = GLToolbarItem::Default_Action_Callback;
+    item.left.action_callback   = [this]() { m_imgui_search_hovered_pos = -1; }; //GLToolbarItem::Default_Action_Callback;
     item.visibility_callback    = GLToolbarItem::Default_Visibility_Callback;
     item.enabling_callback      = GLToolbarItem::Default_Enabling_Callback;
     if (!m_main_toolbar.add_item(item))
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 60f62636d..2f92012f5 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -505,6 +505,7 @@ private:
 #endif // ENABLE_RENDER_STATISTICS
 
     mutable int m_imgui_undo_redo_hovered_pos{ -1 };
+    mutable size_t m_imgui_search_hovered_pos{ size_t(-1) };
     int m_selected_extruder;
 
     Labels m_labels;
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index a9b51874a..dd071a86d 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -539,8 +539,69 @@ static bool selectable(const char* label, bool selected, ImGuiSelectableFlags fl
     return pressed;
 }
 
+// Scroll so that the hovered item is at the top of the window
+static void scroll_y(int hover_id)
+{
+    if (hover_id < 0)
+        return;
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+
+    float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
+    float item_delta = 0.5 * item_size_y;
+
+    float item_top = item_size_y * hover_id;
+    float item_bottom = item_top + item_size_y;
+
+    float win_top = window->Scroll.y;
+    float win_bottom = window->Scroll.y + window->Size.y;
+
+    if (item_bottom + item_delta >= win_bottom)
+        ImGui::SetScrollY(win_top + item_size_y);
+    else if (item_top - item_delta <= win_top)
+        ImGui::SetScrollY(win_top - item_size_y);
+}
+
+// Scroll up for one item 
+static void scroll_up()
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+
+    float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
+    float win_top = window->Scroll.y;
+
+    ImGui::SetScrollY(win_top - item_size_y);
+}
+
+// Scroll down for one item 
+static void scroll_down()
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+
+    float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
+    float win_top = window->Scroll.y;
+
+    ImGui::SetScrollY(win_top + item_size_y);
+}
+
+// Use this function instead of ImGui::IsKeyPressed.
+// ImGui::IsKeyPressed is related for *GImGui.IO.KeysDownDuration[user_key_index]
+// And after first key pressing IsKeyPressed() return "true" always even if key wasn't pressed
+static void process_key_down(ImGuiKey imgui_key, std::function<void()> f)
+{
+    if (ImGui::IsKeyDown(ImGui::GetKeyIndex(imgui_key)))
+    {
+        f();
+        // set KeysDown to false to avoid redundant key down processing
+        ImGuiContext& g = *GImGui;
+        g.IO.KeysDown[ImGui::GetKeyIndex(imgui_key)] = false;
+    }
+}
+
 void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char**), char* search_str,
-                               bool& category, bool& group, size_t& selected, bool& edited, bool& check_changed)
+                               bool& category, bool& group, size_t& hovered_id, size_t& selected, bool& edited, bool& check_changed)
 {
     // ImGui::ListBoxHeader("", size);
     {   
@@ -581,30 +642,64 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
         ImGui::InputTextEx("", NULL, search_str, 20, search_size, 0, NULL, NULL);
         edited = ImGui::IsItemEdited();
 
-        if (ImGui::IsItemDeactivated() && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape))) {
+        process_key_down(ImGuiKey_Escape, [&selected, search_str, str]() {
             // use 9999 to mark selection as a Esc key
             selected = 9999;
             // ... and when Esc key was pressed, than revert search_str value
             strcpy(search_str, str.c_str());
-        }
+        });
 
         ImGui::BeginChildFrame(id, frame_bb.GetSize());
     }
 
     size_t i = 0;
     const char* item_text;
+    int mouse_hovered = -1;
+
     while (items_getter(i, &item_text))
     {
-        selectable(item_text, false);
+        selectable(item_text, i == hovered_id);
 
-        if (ImGui::IsItemHovered())
+        if (ImGui::IsItemHovered()) {
             ImGui::SetTooltip("%s", item_text);
+            hovered_id = size_t(-1);
+            mouse_hovered = i;
+        }
 
         if (ImGui::IsItemClicked())
             selected = i;
         i++;
     }
 
+    scroll_y(mouse_hovered);
+
+    // process Up/DownArrows and Enter
+    process_key_down(ImGuiKey_UpArrow, [&hovered_id, mouse_hovered]() {
+        if (mouse_hovered > 0)
+            scroll_up();
+        else {
+            if (hovered_id > 0 && hovered_id != size_t(-1))
+                --hovered_id;
+            scroll_y(hovered_id);
+        }
+    });
+
+    process_key_down(ImGuiKey_DownArrow, [&hovered_id, mouse_hovered, i]() {
+        if (mouse_hovered > 0)
+            scroll_down();
+        else {
+            if (hovered_id == size_t(-1))
+                hovered_id = 0;
+            else if (hovered_id < size_t(i - 1))
+                ++hovered_id;
+            scroll_y(hovered_id);
+        }
+    });
+
+    process_key_down(ImGuiKey_Enter, [&selected, hovered_id]() {
+        selected = hovered_id;
+    });
+
     ImGui::ListBoxFooter();
 
     // add checkboxes for show/hide Categories and Groups
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index 781d2a25f..781b6ae4f 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -75,7 +75,7 @@ public:
     bool combo(const wxString& label, const std::vector<std::string>& options, int& selection);   // Use -1 to not mark any option as selected
     bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
     void search_list(const ImVec2& size, bool (*items_getter)(int, const char**), char* search_str, 
-                     bool& category, bool& group, size_t& selected, bool& edited, bool& check_changed);
+                     bool& category, bool& group, size_t& hovered_id, size_t& selected, bool& edited, bool& check_changed);
 
     void disabled_begin(bool disabled);
     void disabled_end();
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 0d020d8d8..b58d7f7a6 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -10,6 +10,8 @@
 
 #include <wx/combo.h>
 
+#include <wx/popupwin.h>
+
 #include "Preset.hpp"
 #include "wxExtensions.hpp"
 
@@ -175,7 +177,6 @@ public:
 };
 
 
-#include <wx/popupwin.h>
 
 class PopupSearchList : public wxPopupTransientWindow
 {

From 83782e59b671e867389fecae81161851f902d94a Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 20 Apr 2020 21:46:16 +0200
Subject: [PATCH 29/55] Search: Implemented "Option type" checkbox for imGui
 window on Plater

+ code refactoring
---
 src/slic3r/GUI/GLCanvas3D.cpp   | 15 +++++------
 src/slic3r/GUI/GLCanvas3D.hpp   |  1 -
 src/slic3r/GUI/ImGuiWrapper.cpp | 44 ++++++++++++++++++---------------
 src/slic3r/GUI/ImGuiWrapper.hpp |  8 ++++--
 src/slic3r/GUI/Plater.cpp       |  4 +--
 src/slic3r/GUI/Plater.hpp       |  2 +-
 src/slic3r/GUI/Search.cpp       | 39 +++++++++++++++++++++--------
 src/slic3r/GUI/Search.hpp       | 18 +++++++++++---
 8 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 64338ce30..e7075298c 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4463,9 +4463,9 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const
 }
 
 // Getter for the const char*[] for the search list 
-static bool search_string_getter(int idx, const char** out_text)
+static bool search_string_getter(int idx, const char** label, const char** tooltip)
 {
-    return wxGetApp().plater()->search_string_getter(idx, out_text);
+    return wxGetApp().plater()->search_string_getter(idx, label, tooltip);
 }
 
 bool GLCanvas3D::_render_search_list(float pos_x) const
@@ -4482,7 +4482,7 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
     std::string title = L("Search");
     imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
 
-    size_t selected = size_t(-1);
+    int selected = -1;
     bool edited = false;
     bool check_changed = false;
     float em = static_cast<float>(wxGetApp().em_unit());
@@ -4497,17 +4497,14 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
     strcpy(s, search_line.empty() ? _u8L("Type here to search").c_str() : search_line.c_str());
 
     imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, 
-                       sidebar.get_searcher().category, sidebar.get_searcher().group,
-                       m_imgui_search_hovered_pos,
+                       sidebar.get_searcher().view_params,
                        selected, edited, check_changed);
 
     search_line = s;
     delete [] s;
 
-    if (edited) {
+    if (edited)
         sidebar.search_and_apply_tab_search_lines();
-        m_imgui_search_hovered_pos = -1;
-    }
 
     if (check_changed) {
         if (search_line == _u8L("Type here to search"))
@@ -5068,7 +5065,7 @@ bool GLCanvas3D::_init_main_toolbar()
                 _deactivate_search_toolbar_item();
         }
     };
-    item.left.action_callback   = [this]() { m_imgui_search_hovered_pos = -1; }; //GLToolbarItem::Default_Action_Callback;
+    item.left.action_callback   = GLToolbarItem::Default_Action_Callback;
     item.visibility_callback    = GLToolbarItem::Default_Visibility_Callback;
     item.enabling_callback      = GLToolbarItem::Default_Enabling_Callback;
     if (!m_main_toolbar.add_item(item))
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 2f92012f5..60f62636d 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -505,7 +505,6 @@ private:
 #endif // ENABLE_RENDER_STATISTICS
 
     mutable int m_imgui_undo_redo_hovered_pos{ -1 };
-    mutable size_t m_imgui_search_hovered_pos{ size_t(-1) };
     int m_selected_extruder;
 
     Labels m_labels;
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index dd071a86d..435dfbe60 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -25,6 +25,7 @@
 #include "3DScene.hpp"
 #include "GUI.hpp"
 #include "I18N.hpp"
+#include "Search.hpp"
 
 namespace Slic3r {
 namespace GUI {
@@ -600,8 +601,8 @@ static void process_key_down(ImGuiKey imgui_key, std::function<void()> f)
     }
 }
 
-void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char**), char* search_str,
-                               bool& category, bool& group, size_t& hovered_id, size_t& selected, bool& edited, bool& check_changed)
+void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
+                               Search::OptionViewParameters& view_params, int& selected, bool& edited, bool& check_changed)
 {
     // ImGui::ListBoxHeader("", size);
     {   
@@ -641,6 +642,8 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
         std::string str = search_str;
         ImGui::InputTextEx("", NULL, search_str, 20, search_size, 0, NULL, NULL);
         edited = ImGui::IsItemEdited();
+        if (edited)
+            view_params.hovered_id = -1;
 
         process_key_down(ImGuiKey_Escape, [&selected, search_str, str]() {
             // use 9999 to mark selection as a Esc key
@@ -652,17 +655,19 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
         ImGui::BeginChildFrame(id, frame_bb.GetSize());
     }
 
-    size_t i = 0;
+    int i = 0;
     const char* item_text;
+    const char* tooltip;
     int mouse_hovered = -1;
+    int& hovered_id = view_params.hovered_id;
 
-    while (items_getter(i, &item_text))
+    while (items_getter(i, &item_text, &tooltip))
     {
         selectable(item_text, i == hovered_id);
 
         if (ImGui::IsItemHovered()) {
-            ImGui::SetTooltip("%s", item_text);
-            hovered_id = size_t(-1);
+            ImGui::SetTooltip("%s", /*item_text*/tooltip);
+            view_params.hovered_id = -1;
             mouse_hovered = i;
         }
 
@@ -702,22 +707,21 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
 
     ImGui::ListBoxFooter();
 
+    auto check_box = [&check_changed, this](const wxString& label, bool& check) {
+        ImGui::SameLine();
+        bool ch = check;
+        checkbox(label, ch);
+        if (ImGui::IsItemClicked()) {
+            check = !check;
+            check_changed = true;
+        }
+    };
+
     // add checkboxes for show/hide Categories and Groups
     text(_L("Use for search")+":");
-    ImGui::SameLine();
-    bool cat = category;
-    checkbox(_L("Category"), cat);
-    if (ImGui::IsItemClicked()) {
-        category = !category;
-        check_changed = true;
-    }
-    ImGui::SameLine();
-    bool gr = group;
-    checkbox(_L("Group"), gr);
-    if (ImGui::IsItemClicked()) {
-        group = !group;
-        check_changed = true;
-    }
+    check_box(_L("Type"),       view_params.type);
+    check_box(_L("Category"),   view_params.category);
+    check_box(_L("Group"),      view_params.group);
 }
 
 void ImGuiWrapper::disabled_begin(bool disabled)
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index 781b6ae4f..d32604963 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -8,6 +8,10 @@
 
 #include "libslic3r/Point.hpp"
 
+namespace Slic3r {namespace Search {
+struct OptionViewParameters;
+}}
+
 class wxString;
 class wxMouseEvent;
 class wxKeyEvent;
@@ -74,8 +78,8 @@ public:
     bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
     bool combo(const wxString& label, const std::vector<std::string>& options, int& selection);   // Use -1 to not mark any option as selected
     bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
-    void search_list(const ImVec2& size, bool (*items_getter)(int, const char**), char* search_str, 
-                     bool& category, bool& group, size_t& hovered_id, size_t& selected, bool& edited, bool& check_changed);
+    void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
+                     Search::OptionViewParameters& view_params, int& selected, bool& edited, bool& check_changed);
 
     void disabled_begin(bool disabled);
     void disabled_end();
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 6cea1a7ef..f79ebec27 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -5343,12 +5343,12 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou
     out_text = "";
 }
 
-bool Plater::search_string_getter(int idx, const char** out_text)
+bool Plater::search_string_getter(int idx, const char** label, const char** tooltip)
 {
     const Search::OptionsSearcher& search_list = p->sidebar->get_searcher();
     
     if (0 <= idx && (size_t)idx < search_list.size()) {
-        search_list[idx].get_marked_label(out_text);
+        search_list[idx].get_marked_label_and_tooltip(label, tooltip);
         return true;
     }
 
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index ae7b241d9..33a279ed6 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -243,7 +243,7 @@ public:
     void redo_to(int selection);
     bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text);
     void undo_redo_topmost_string_getter(const bool is_undo, std::string& out_text);
-    bool search_string_getter(int idx, const char **out_text);
+    bool search_string_getter(int idx, const char** label, const char** tooltip);
     // For the memory statistics. 
     const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
     // Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo.
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index ecc002476..eb62b8a3f 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -23,6 +23,14 @@ using GUI::into_u8;
 
 namespace Search {
 
+static std::map<Preset::Type, std::string> NameByType = {
+    { Preset::TYPE_PRINT,           L("Print")     },
+    { Preset::TYPE_FILAMENT,        L("Filament")  },
+    { Preset::TYPE_SLA_MATERIAL,    L("Material")  },
+    { Preset::TYPE_SLA_PRINT,       L("Print")     },
+    { Preset::TYPE_PRINTER,         L("Printer")   }
+};
+
 FMFlag Option::fuzzy_match_simple(char const * search_pattern) const
 {
     return  fts::fuzzy_match_simple(search_pattern, label_local.utf8_str())     ? fmLabelLocal      :
@@ -86,9 +94,10 @@ void FoundOption::get_label(const char** out_text) const
     *out_text = label.utf8_str();
 }
 
-void FoundOption::get_marked_label(const char** out_text) const
+void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const
 {
-    *out_text = marked_label.utf8_str();
+    *label_   = marked_label.utf8_str();
+    *tooltip_ = tooltip.utf8_str();
 }
 
 template<class T>
@@ -214,24 +223,34 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
     found.clear();
 
     bool full_list = search.empty();
+    wxString sep = " : ";
 
-    auto get_label = [this](const Option& opt)
+    auto get_label = [this, sep](const Option& opt)
     {
         wxString label;
-        if (category)
-            label += opt.category_local + " > ";
-        if (group)
-            label += opt.group_local + " > ";
+        if (view_params.type)
+            label += _(NameByType[opt.type]) + sep;
+        if (view_params.category)
+            label += opt.category_local + sep;
+        if (view_params.group)
+            label += opt.group_local + sep;
         label += opt.label_local;
         return label;
     };
 
+    auto get_tooltip = [this, sep](const Option& opt)
+    {
+        return  _(NameByType[opt.type]) + sep +
+                opt.category_local + sep +
+                opt.group_local + sep + opt.label_local;
+    };
+
     for (size_t i=0; i < options.size(); i++)
     {
         const Option &opt = options[i];
         if (full_list) {
             wxString label = get_label(opt);
-            found.emplace_back(FoundOption{ label, label, i, 0 });
+            found.emplace_back(FoundOption{ label, label, get_tooltip(opt), i, 0 });
             continue;
         }
 
@@ -240,7 +259,7 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
         FMFlag fuzzy_match_flag = opt.fuzzy_match(search, score);
         if (fuzzy_match_flag != fmUndef)
         {
-            wxString label = get_label(opt); //opt.category_local + " > " + opt.group_local  + " > " + opt.label_local;
+            wxString label = get_label(opt);
 
             if (     fuzzy_match_flag == fmLabel   ) label += "(" + opt.label    + ")";
             else if (fuzzy_match_flag == fmGroup   ) label += "(" + opt.group    + ")";
@@ -251,7 +270,7 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
             mark_string(marked_label, from_u8(search));
             clear_marked_string(marked_label);
 
-            found.emplace_back(FoundOption{ label, marked_label, i, score });
+            found.emplace_back(FoundOption{ label, marked_label, get_tooltip(opt), i, score });
         }
     }
 
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index b58d7f7a6..7933527af 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -11,6 +11,7 @@
 #include <wx/combo.h>
 
 #include <wx/popupwin.h>
+#include <wx/checkbox.h>
 
 #include "Preset.hpp"
 #include "wxExtensions.hpp"
@@ -69,11 +70,21 @@ struct Option {
 struct FoundOption {
     wxString        label;
     wxString        marked_label;
+    wxString        tooltip;
     size_t          option_idx {0};
     int             outScore {0};
 
     void get_label(const char** out_text) const;
-    void get_marked_label(const char** out_text) const;
+    void get_marked_label_and_tooltip(const char** label, const char** tooltip) const;
+};
+
+struct OptionViewParameters
+{
+    bool type       {false};
+    bool category   {false};
+    bool group      {true };
+
+    int  hovered_id {-1};
 };
 
 class OptionsSearcher
@@ -96,11 +107,10 @@ class OptionsSearcher
     };
 
     size_t options_size() const { return options.size(); }
-    size_t found_size() const { return found.size(); }
+    size_t found_size()   const { return found.size(); }
 
 public:
-    bool                category{ false };
-    bool                group{ true };
+    OptionViewParameters                    view_params;
 
     void init(std::vector<InputInfo> input_values);
     void apply(DynamicPrintConfig *config,

From 03eb5ffcd5d71181e2c0533060fd94a6601be76f Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 22 Apr 2020 10:54:11 +0200
Subject: [PATCH 30/55] WIP: Reworking of FillRectilinear2 to support
 monotonous infill with ant colony optimization and 3-opt flips.

---
 src/libslic3r/Fill/FillBase.hpp         |    6 +
 src/libslic3r/Fill/FillRectilinear2.cpp | 2007 ++++++++++++++++-------
 src/libslic3r/libslic3r.h               |    1 +
 3 files changed, 1445 insertions(+), 569 deletions(-)

diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp
index 5a9e92739..9fb37d2c0 100644
--- a/src/libslic3r/Fill/FillBase.hpp
+++ b/src/libslic3r/Fill/FillBase.hpp
@@ -5,6 +5,7 @@
 #include <memory.h>
 #include <float.h>
 #include <stdint.h>
+#include <stdexcept>
 
 #include <type_traits>
 
@@ -18,6 +19,11 @@ namespace Slic3r {
 class ExPolygon;
 class Surface;
 
+class InfillFailedException : public std::runtime_error {
+public:
+    InfillFailedException() : std::runtime_error("Infill failed") {}
+};
+
 struct FillParams
 {
     bool        full_infill() const { return density > 0.9999f; }
diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp
index 8aea75886..c2c19046e 100644
--- a/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -4,7 +4,9 @@
 #include <algorithm>
 #include <cmath>
 #include <limits>
+#include <random>
 
+#include <boost/container/small_vector.hpp>
 #include <boost/static_assert.hpp>
 
 #include "../ClipperUtils.hpp"
@@ -105,26 +107,15 @@ static inline void polygon_segment_append_reversed(Points &out, const Polygon &p
 }
 
 // Intersection point of a vertical line with a polygon segment.
-class SegmentIntersection
+struct SegmentIntersection
 {
-public:
-    SegmentIntersection() : 
-        iContour(0),
-        iSegment(0),
-        pos_p(0),
-        pos_q(1),
-        type(UNKNOWN),
-        consumed_vertical_up(false),
-        consumed_perimeter_right(false)
-        {}
-
     // Index of a contour in ExPolygonWithOffset, with which this vertical line intersects.
-    size_t      iContour;
+    size_t      iContour { 0 };
     // Index of a segment in iContour, with which this vertical line intersects.
-    size_t      iSegment;
-    // y position of the intersection, ratinal number.
-    int64_t     pos_p;
-    uint32_t    pos_q;
+    size_t      iSegment { 0 };
+    // y position of the intersection, rational number.
+    int64_t     pos_p { 0 };
+    uint32_t    pos_q { 1 };
 
     coord_t     pos() const {
         // Division rounds both positive and negative down to zero.
@@ -141,30 +132,140 @@ public:
     // A vertical segment will be at least intersected by OUTER_LOW, OUTER_HIGH,
     // but it could be intersected with OUTER_LOW, INNER_LOW, INNER_HIGH, OUTER_HIGH,
     // and there may be more than one pair of INNER_LOW, INNER_HIGH between OUTER_LOW, OUTER_HIGH.
-    enum SegmentIntersectionType {
+    enum SegmentIntersectionType : char {
         OUTER_LOW   = 0,
         OUTER_HIGH  = 1,
         INNER_LOW   = 2,
         INNER_HIGH  = 3,
         UNKNOWN     = -1
     };
-    SegmentIntersectionType type;
+    SegmentIntersectionType type { UNKNOWN };
 
+    // Left vertical line / contour intersection point.
+    // null if next_on_contour_vertical.
+    int32_t	prev_on_contour { 0 };
+    // Right vertical line / contour intersection point.
+    // If next_on_contour_vertical, then then next_on_contour contains next contour point on the same vertical line.
+    int32_t	next_on_contour { 0 };
+
+    enum class LinkType : uint8_t {
+    	// Horizontal link (left or right).
+    	Horizontal,
+    	// Vertical link, up.
+    	Up,
+    	// Vertical link, down.
+    	Down
+    };
+
+    enum class LinkQuality : uint8_t {
+    	Invalid,
+        Valid,
+    	// Valid link, to be followed when extruding.
+    	// Link inside a monotonous region.
+    	ValidMonotonous,
+    	// Valid link, to be possibly followed when extruding.
+    	// Link between two monotonous regions.
+    	ValidNonMonotonous,
+    	// Link from T to end of another contour.
+    	FromT,
+    	// Link from end of one contour to T.
+    	ToT,
+    	// Link from one T to another T, making a letter H.
+    	H,
+    	// Vertical segment
+    	TooLong,
+    };
+
+    // Kept grouped with other booleans for smaller memory footprint.
+    LinkType 		prev_on_contour_type { LinkType::Horizontal };
+    LinkType 		next_on_contour_type { LinkType::Horizontal };
+    LinkQuality 	prev_on_contour_quality { true };
+    LinkQuality 	next_on_contour_quality { true };
     // Was this segment along the y axis consumed?
     // Up means up along the vertical segment.
-    bool consumed_vertical_up;
+    bool 	 		consumed_vertical_up { false };
     // Was a segment of the inner perimeter contour consumed?
     // Right means right from the vertical segment.
-    bool consumed_perimeter_right;
+    bool 	 		consumed_perimeter_right { false };
 
     // For the INNER_LOW type, this point may be connected to another INNER_LOW point following a perimeter contour.
     // For the INNER_HIGH type, this point may be connected to another INNER_HIGH point following a perimeter contour.
     // If INNER_LOW is connected to INNER_HIGH or vice versa,
     // one has to make sure the vertical infill line does not overlap with the connecting perimeter line.
-    bool is_inner() const { return type == INNER_LOW  || type == INNER_HIGH; }
-    bool is_outer() const { return type == OUTER_LOW  || type == OUTER_HIGH; }
-    bool is_low  () const { return type == INNER_LOW  || type == OUTER_LOW; }
-    bool is_high () const { return type == INNER_HIGH || type == OUTER_HIGH; }
+    bool 	is_inner() const { return type == INNER_LOW  || type == INNER_HIGH; }
+    bool 	is_outer() const { return type == OUTER_LOW  || type == OUTER_HIGH; }
+    bool 	is_low  () const { return type == INNER_LOW  || type == OUTER_LOW; }
+    bool 	is_high () const { return type == INNER_HIGH || type == OUTER_HIGH; }
+
+    enum class Side {
+    	Left,
+    	Right
+    };
+    enum class Direction {
+    	Up,
+    	Down
+    };
+
+    bool 	has_left_horizontal()    		 	const { return this->prev_on_contour_type == LinkType::Horizontal; }
+    bool 	has_right_horizontal()   		 	const { return this->next_on_contour_type == LinkType::Horizontal; }
+    bool 	has_horizontal(Side side)		 	const { return side == Side::Left ? this->has_left_horizontal() : this->has_right_horizontal(); }
+
+    bool 	has_left_vertical_up()   		 	const { return this->prev_on_contour_type == LinkType::Up; }
+    bool 	has_left_vertical_down() 		 	const { return this->prev_on_contour_type == LinkType::Down; }
+    bool 	has_left_vertical(Direction dir) 	const { return dir == Direction::Up ? this->has_left_vertical_up() : this->has_left_vertical_down(); }
+    bool 	has_left_vertical()    	 		 	const { return this->has_left_vertical_up() || this->has_left_vertical_down(); }
+    bool 	has_left_vertical_outside()			const { return this->is_low() ? this->has_left_vertical_down() : this->has_left_vertical_up(); }
+
+    bool 	has_right_vertical_up()   			const { return this->next_on_contour_type == LinkType::Up; }
+    bool 	has_right_vertical_down() 			const { return this->next_on_contour_type == LinkType::Down; }
+    bool 	has_right_vertical(Direction dir) 	const { return dir == Direction::Up ? this->has_right_vertical_up() : this->has_right_vertical_down(); }
+    bool 	has_right_vertical()    			const { return this->has_right_vertical_up() || this->has_right_vertical_down(); }
+    bool 	has_right_vertical_outside()		const { return this->is_low() ? this->has_right_vertical_down() : this->has_right_vertical_up(); }
+
+    bool 	has_vertical()						const { return this->has_left_vertical() || this->has_right_vertical(); }
+    bool 	has_vertical(Side side)				const { return side == Side::Left ? this->has_left_vertical() : this->has_right_vertical(); }
+    bool 	has_vertical_up()					const { return this->has_left_vertical_up() || this->has_right_vertical_up(); }
+    bool 	has_vertical_down()					const { return this->has_left_vertical_down() || this->has_right_vertical_down(); }
+    bool 	has_vertical(Direction dir)			const { return dir == Direction::Up ? this->has_vertical_up() : this->has_vertical_down(); }
+
+    int 	left_horizontal()  					const { return this->has_left_horizontal() 	? this->prev_on_contour : -1; }
+    int 	right_horizontal()  				const { return this->has_right_horizontal() ? this->next_on_contour : -1; }
+    int 	horizontal(Side side)  				const { return side == Side::Left ? this->left_horizontal() : this->right_horizontal(); }
+
+    int 	left_vertical_up()   		 		const { return this->has_left_vertical_up()    ? this->prev_on_contour : -1; }
+    int 	left_vertical_down()   		 		const { return this->has_left_vertical_down()  ? this->prev_on_contour : -1; }
+    int 	left_vertical(Direction dir) 		const { return (dir == Direction::Up ? this->has_left_vertical_up() : this->has_left_vertical_down()) ? this->prev_on_contour : -1; }
+    int 	left_vertical()   			 		const { return this->has_left_vertical() 	   ? this->prev_on_contour : -1; }
+    int 	left_vertical_outside()				const { return this->is_low() ? this->left_vertical_down() : this->left_vertical_up(); }
+    int 	right_vertical_up()   		 		const { return this->has_right_vertical_up()   ? this->prev_on_contour : -1; }
+    int 	right_vertical_down()   	 		const { return this->has_right_vertical_down() ? this->prev_on_contour : -1; }
+    int 	right_vertical(Direction dir) 		const { return (dir == Direction::Up ? this->has_right_vertical_up() : this->has_right_vertical_down()) ? this->next_on_contour : -1; }
+    int 	right_vertical()   			 		const { return this->has_right_vertical() 	   ? this->prev_on_contour : -1; }
+    int 	right_vertical_outside()			const { return this->is_low() ? this->right_vertical_down() : this->right_vertical_up(); }
+
+    int 	vertical_up(Side side)				const { return side == Side::Left ? this->left_vertical_up() : this->right_vertical_up(); }
+    int 	vertical_down(Side side)			const { return side == Side::Left ? this->left_vertical_down() : this->right_vertical_down(); }
+    int 	vertical_outside(Side side)			const { return side == Side::Left ? this->left_vertical_outside() : this->right_vertical_outside(); }
+    int 	vertical_up()						const { 
+    	assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up());
+    	return this->has_left_vertical_up() ? this->left_vertical_up() : this->right_vertical_up();
+    }
+    LinkQuality vertical_up_quality()			const {
+    	assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up());
+    	return this->has_left_vertical_up() ? this->prev_on_contour_quality : this->next_on_contour_quality;
+    }
+    int 	vertical_down()						const {
+    	assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down());
+    	return this->has_left_vertical_down() ? this->left_vertical_down() : this->right_vertical_down();
+    }
+    LinkQuality vertical_down_quality()			const {
+    	assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down());
+    	return this->has_left_vertical_down() ? this->prev_on_contour_quality : this->next_on_contour_quality;
+    }
+    int 	vertical_outside()					const { return this->is_low() ? this->vertical_down() : this->vertical_up(); }
+
+//    int  	next_up()    const { return this->prev_on_contour_vertical ? -1 : this->prev_on_contour; }
+//    int  	next_right() const { return this->next_on_contour_vertical ? -1 : this->next_on_contour; }
 
     // Compare two y intersection points given by rational numbers.
     // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32.
@@ -250,11 +351,11 @@ public:
         return l_hi + (l_lo >> 32) == r_hi + (r_lo >> 32);
     }
 };
+static_assert(sizeof(SegmentIntersection::pos_q) == 4, "SegmentIntersection::pos_q has to be 32bit long!");
 
 // A vertical line with intersection points with polygons.
-class SegmentedIntersectionLine
+struct SegmentedIntersectionLine
 {
-public:
     // Index of this vertical intersection line.
     size_t                              idx;
     // x position of this vertical intersection line.
@@ -290,10 +391,10 @@ public:
 //        bool sticks_removed = 
         remove_sticks(polygons_src);
 //        if (sticks_removed) printf("Sticks removed!\n");
-        polygons_outer = offset(polygons_src, aoffset1,
+        polygons_outer = offset(polygons_src, float(aoffset1),
             ClipperLib::jtMiter,
             mitterLimit);
-        polygons_inner = offset(polygons_outer, aoffset2 - aoffset1,
+        polygons_inner = offset(polygons_outer, float(aoffset2 - aoffset1),
             ClipperLib::jtMiter,
             mitterLimit);
 		// Filter out contours with zero area or small area, contours with 2 points only.
@@ -364,97 +465,6 @@ static inline int distance_of_segmens(const Polygon &poly, size_t seg1, size_t s
     return d;
 }
 
-// For a vertical line, an inner contour and an intersection point,
-// find an intersection point on the previous resp. next vertical line.
-// The intersection point is connected with the prev resp. next intersection point with iInnerContour.
-// Return -1 if there is no such point on the previous resp. next vertical line.
-static inline int intersection_on_prev_next_vertical_line(
-    const ExPolygonWithOffset                     &poly_with_offset,
-    const std::vector<SegmentedIntersectionLine>  &segs,
-    size_t                                         iVerticalLine,
-    size_t                                         iInnerContour,
-    size_t                                         iIntersection,
-    bool                                           dir_is_next)
-{
-    size_t iVerticalLineOther = iVerticalLine;
-    if (dir_is_next) {
-        if (++ iVerticalLineOther == segs.size())
-            // No successive vertical line.
-            return -1;
-    } else if (iVerticalLineOther -- == 0) {
-        // No preceding vertical line.
-        return -1;
-    }
-
-    const SegmentedIntersectionLine &il    = segs[iVerticalLine];
-    const SegmentIntersection       &itsct = il.intersections[iIntersection];
-    const SegmentedIntersectionLine &il2   = segs[iVerticalLineOther];
-    const Polygon                   &poly  = poly_with_offset.contour(iInnerContour);
-//    const bool                       ccw   = poly_with_offset.is_contour_ccw(iInnerContour);
-    const bool                       forward = itsct.is_low() == dir_is_next;
-    // Resulting index of an intersection point on il2.
-    int                              out   = -1;
-    // Find an intersection point on iVerticalLineOther, intersecting iInnerContour
-    // at the same orientation as iIntersection, and being closest to iIntersection
-    // in the number of contour segments, when following the direction of the contour.
-    int                              dmin  = std::numeric_limits<int>::max();
-    for (size_t i = 0; i < il2.intersections.size(); ++ i) {
-        const SegmentIntersection &itsct2 = il2.intersections[i];
-        if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
-            /*
-            if (itsct.is_low()) {
-                assert(itsct.type == SegmentIntersection::INNER_LOW);
-                assert(iIntersection > 0);
-                assert(il.intersections[iIntersection-1].type == SegmentIntersection::OUTER_LOW);                
-                assert(i > 0);
-                if (il2.intersections[i-1].is_inner())
-                    // Take only the lowest inner intersection point.
-                    continue;
-                assert(il2.intersections[i-1].type == SegmentIntersection::OUTER_LOW);
-            } else {
-                assert(itsct.type == SegmentIntersection::INNER_HIGH);
-                assert(iIntersection+1 < il.intersections.size());
-                assert(il.intersections[iIntersection+1].type == SegmentIntersection::OUTER_HIGH);
-                assert(i+1 < il2.intersections.size());
-                if (il2.intersections[i+1].is_inner())
-                    // Take only the highest inner intersection point.
-                    continue;
-                assert(il2.intersections[i+1].type == SegmentIntersection::OUTER_HIGH);
-            }
-            */
-            // The intersection points lie on the same contour and have the same orientation.
-            // Find the intersection point with a shortest path in the direction of the contour.
-            int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, forward);
-            if (d < dmin) {
-                out = i;
-                dmin = d;
-            }
-        }
-    }
-    //FIXME this routine is not asymptotic optimal, it will be slow if there are many intersection points along the line.
-    return out;
-}
-
-static inline int intersection_on_prev_vertical_line(
-    const ExPolygonWithOffset                     &poly_with_offset, 
-    const std::vector<SegmentedIntersectionLine>  &segs,
-    size_t                                         iVerticalLine,
-    size_t                                         iInnerContour,
-    size_t                                         iIntersection)
-{
-    return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, false);
-}
-
-static inline int intersection_on_next_vertical_line(
-    const ExPolygonWithOffset                     &poly_with_offset, 
-    const std::vector<SegmentedIntersectionLine>  &segs, 
-    size_t                                         iVerticalLine, 
-    size_t                                         iInnerContour, 
-    size_t                                         iIntersection)
-{
-    return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, true);
-}
-
 enum IntersectionTypeOtherVLine {
     // There is no connection point on the other vertical line.
     INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED = -1,
@@ -477,17 +487,19 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
     const std::vector<SegmentedIntersectionLine>  &segs,
     size_t                                         iVerticalLine,
     size_t                                         iIntersection,
-    size_t                                         iIntersectionOther,
-    bool                                           dir_is_next)
+    SegmentIntersection::Side                      side)
 {
-    // This routine will propose a connecting line even if the connecting perimeter segment intersects 
-    // iVertical line multiple times before reaching iIntersectionOther.
-    if (iIntersectionOther == size_t(-1))
-        return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
-    assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
     const SegmentedIntersectionLine &il_this      = segs[iVerticalLine];
     const SegmentIntersection       &itsct_this   = il_this.intersections[iIntersection];
-    const SegmentedIntersectionLine &il_other     = segs[dir_is_next ? (iVerticalLine+1) : (iVerticalLine-1)];
+	if (itsct_this.has_vertical(side))
+	    // Not the first intersection along the contor. This intersection point
+	    // has been preceded by an intersection point along the vertical line.
+		return INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST;
+    int iIntersectionOther = itsct_this.horizontal(side);
+    if (iIntersectionOther == -1)
+        return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
+    assert(side == SegmentIntersection::Side::Right ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
+    const SegmentedIntersectionLine &il_other     = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine+1) : (iVerticalLine-1)];
     const SegmentIntersection       &itsct_other  = il_other.intersections[iIntersectionOther];
     assert(itsct_other.is_inner());
     assert(iIntersectionOther > 0);
@@ -499,7 +511,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
         // Only perimeter segments connecting to the end of a vertical segment are followed.
         return INTERSECTION_TYPE_OTHER_VLINE_INNER;
     assert(itsct_other.is_low() == itsct_other2.is_low());
-    if (dir_is_next ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right)
+    if (side == SegmentIntersection::Side::Right ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right)
         // This perimeter segment was already consumed.
         return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED;
     if (itsct_other.is_low() ? itsct_other.consumed_vertical_up : il_other.intersections[iIntersectionOther-1].consumed_vertical_up)
@@ -511,19 +523,17 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
 static inline IntersectionTypeOtherVLine intersection_type_on_prev_vertical_line(
     const std::vector<SegmentedIntersectionLine>  &segs, 
     size_t                                         iVerticalLine, 
-    size_t                                         iIntersection,
-    size_t                                         iIntersectionPrev)
+    size_t                                         iIntersection)
 {
-    return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, iIntersectionPrev, false);
+    return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Left);
 }
 
 static inline IntersectionTypeOtherVLine intersection_type_on_next_vertical_line(
     const std::vector<SegmentedIntersectionLine>  &segs, 
     size_t                                         iVerticalLine, 
-    size_t                                         iIntersection,
-    size_t                                         iIntersectionNext)
+    size_t                                         iIntersection)
 {
-    return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, iIntersectionNext, true);
+    return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Right);
 }
 
 // Measure an Euclidian length of a perimeter segment when going from iIntersection to iIntersection2.
@@ -531,7 +541,6 @@ static inline coordf_t measure_perimeter_prev_next_segment_length(
     const ExPolygonWithOffset                     &poly_with_offset, 
     const std::vector<SegmentedIntersectionLine>  &segs,
     size_t                                         iVerticalLine,
-    size_t                                         iInnerContour,
     size_t                                         iIntersection,
     size_t                                         iIntersection2,
     bool                                           dir_is_next)
@@ -550,8 +559,9 @@ static inline coordf_t measure_perimeter_prev_next_segment_length(
     const SegmentIntersection       &itsct  = il.intersections[iIntersection];
     const SegmentedIntersectionLine &il2    = segs[iVerticalLineOther];
     const SegmentIntersection       &itsct2 = il2.intersections[iIntersection2];
-    const Polygon                   &poly   = poly_with_offset.contour(iInnerContour);
-//    const bool                       ccw    = poly_with_offset.is_contour_ccw(iInnerContour);
+    assert(itsct.iContour == itsct2.iContour);
+    const Polygon                   &poly   = poly_with_offset.contour(itsct.iContour);
+//    const bool                       ccw    = poly_with_offset.is_contour_ccw(il.iContour);
     assert(itsct.type == itsct2.type);
     assert(itsct.iContour == itsct2.iContour);
     assert(itsct.is_inner());
@@ -568,22 +578,20 @@ static inline coordf_t measure_perimeter_prev_segment_length(
     const ExPolygonWithOffset                     &poly_with_offset,
     const std::vector<SegmentedIntersectionLine>  &segs,
     size_t                                         iVerticalLine,
-    size_t                                         iInnerContour,
     size_t                                         iIntersection,
     size_t                                         iIntersection2)
 {
-    return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, false);
+    return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, false);
 }
 
 static inline coordf_t measure_perimeter_next_segment_length(
     const ExPolygonWithOffset                     &poly_with_offset,
     const std::vector<SegmentedIntersectionLine>  &segs,
     size_t                                         iVerticalLine,
-    size_t                                         iInnerContour,
     size_t                                         iIntersection,
     size_t                                         iIntersection2)
 {
-    return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, true);
+    return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, true);
 }
 
 // Append the points of a perimeter segment when going from iIntersection to iIntersection2.
@@ -632,7 +640,6 @@ static inline coordf_t measure_perimeter_segment_on_vertical_line_length(
     const ExPolygonWithOffset                     &poly_with_offset,
     const std::vector<SegmentedIntersectionLine>  &segs,
     size_t                                         iVerticalLine,
-    size_t                                         iInnerContour,
     size_t                                         iIntersection,
     size_t                                         iIntersection2,
     bool                                           forward)
@@ -640,11 +647,10 @@ static inline coordf_t measure_perimeter_segment_on_vertical_line_length(
     const SegmentedIntersectionLine &il = segs[iVerticalLine];
     const SegmentIntersection       &itsct = il.intersections[iIntersection];
     const SegmentIntersection       &itsct2 = il.intersections[iIntersection2];
-    const Polygon                   &poly = poly_with_offset.contour(iInnerContour);
+    const Polygon                   &poly = poly_with_offset.contour(itsct.iContour);
     assert(itsct.is_inner());
     assert(itsct2.is_inner());
     assert(itsct.type != itsct2.type);
-    assert(itsct.iContour == iInnerContour);
     assert(itsct.iContour == itsct2.iContour);
     Point p1(il.pos, itsct.pos());
     Point p2(il.pos, itsct2.pos());
@@ -759,80 +765,15 @@ enum DirectionMask
     DIR_BACKWARD = 2
 };
 
-bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams &params, float angleBase, float pattern_shift, Polylines &polylines_out)
+static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(const ExPolygonWithOffset &poly_with_offset, size_t n_vlines, coord_t x0, coord_t line_spacing)
 {
-    // At the end, only the new polylines will be rotated back.
-    size_t n_polylines_out_initial = polylines_out.size();
-
-    // Shrink the input polygon a bit first to not push the infill lines out of the perimeters.
-//    const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
-    const float INFILL_OVERLAP_OVER_SPACING = 0.45f;
-    assert(INFILL_OVERLAP_OVER_SPACING > 0 && INFILL_OVERLAP_OVER_SPACING < 0.5f);
-
-    // Rotate polygons so that we can work with vertical lines here
-    std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
-    rotate_vector.first += angleBase;
-
-    assert(params.density > 0.0001f && params.density <= 1.f);
-    coord_t line_spacing = coord_t(scale_(this->spacing) / params.density);
-
-    // On the polygons of poly_with_offset, the infill lines will be connected.
-    ExPolygonWithOffset poly_with_offset(
-        surface->expolygon, 
-        - rotate_vector.first, 
-        scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing),
-        scale_(this->overlap - 0.5 * this->spacing));
-    if (poly_with_offset.n_contours_inner == 0) {
-        // Not a single infill line fits.
-        //FIXME maybe one shall trigger the gap fill here?
-        return true;
-    }
-
-    BoundingBox bounding_box = poly_with_offset.bounding_box_src();
-
-    // define flow spacing according to requested density
-    if (params.full_infill() && !params.dont_adjust) {
-        line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
-        this->spacing = unscale<double>(line_spacing);
-    } else {
-        // extend bounding box so that our pattern will be aligned with other layers
-        // Transform the reference point to the rotated coordinate system.
-        Point refpt = rotate_vector.second.rotated(- rotate_vector.first);
-        // _align_to_grid will not work correctly with positive pattern_shift.
-        coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing;
-        refpt(0) -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
-        bounding_box.merge(_align_to_grid(
-            bounding_box.min, 
-            Point(line_spacing, line_spacing), 
-            refpt));
-    }
-
-    // Intersect a set of euqally spaced vertical lines wiht expolygon.
-    // n_vlines = ceil(bbox_width / line_spacing)
-    size_t  n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing;
-	coord_t x0 = bounding_box.min(0);
-	if (params.full_infill())
-		x0 += (line_spacing + SCALED_EPSILON) / 2;
-
-#ifdef SLIC3R_DEBUG
-    static int iRun = 0;
-    BoundingBox bbox_svg = poly_with_offset.bounding_box_outer();
-    ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.));
-    poly_with_offset.export_to_svg(svg);
-    {
-        ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-initial-%d.svg", iRun), bbox_svg); // , scale_(1.));
-        poly_with_offset.export_to_svg(svg);
-    }
-    iRun ++;
-#endif /* SLIC3R_DEBUG */
-
-    // For each contour
     // Allocate storage for the segments.
     std::vector<SegmentedIntersectionLine> segs(n_vlines, SegmentedIntersectionLine());
-    for (size_t i = 0; i < n_vlines; ++ i) {
+    for (coord_t i = 0; i < coord_t(n_vlines); ++ i) {
         segs[i].idx = i;
         segs[i].pos = x0 + i * line_spacing;
     }
+    // For each contour
     for (size_t iContour = 0; iContour < poly_with_offset.n_contours; ++ iContour) {
         const Points &contour = poly_with_offset.contour(iContour).points;
         if (contour.size() < 2)
@@ -977,26 +918,1291 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
     }
 
     // Verify the segments. If something is wrong, give up.
-#define ASSERT_OR_RETURN(CONDITION) do { assert(CONDITION); if (! (CONDITION)) return false; } while (0)
+#define ASSERT_THROW(CONDITION) do { assert(CONDITION); throw InfillFailedException(); } while (0)
     for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) {
         SegmentedIntersectionLine &sil = segs[i_seg];
         // The intersection points have to be even.
-        ASSERT_OR_RETURN((sil.intersections.size() & 1) == 0);
+        ASSERT_THROW((sil.intersections.size() & 1) == 0);
         for (size_t i = 0; i < sil.intersections.size();) {
             // An intersection segment crossing the bigger contour may cross the inner offsetted contour even number of times.
-            ASSERT_OR_RETURN(sil.intersections[i].type == SegmentIntersection::OUTER_LOW);
+            ASSERT_THROW(sil.intersections[i].type == SegmentIntersection::OUTER_LOW);
             size_t j = i + 1;
-            ASSERT_OR_RETURN(j < sil.intersections.size());
-            ASSERT_OR_RETURN(sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH);
+            ASSERT_THROW(j < sil.intersections.size());
+            ASSERT_THROW(sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH);
             for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++ j) ;
-            ASSERT_OR_RETURN(j < sil.intersections.size());
-            ASSERT_OR_RETURN((j & 1) == 1);
-            ASSERT_OR_RETURN(sil.intersections[j].type == SegmentIntersection::OUTER_HIGH);
-            ASSERT_OR_RETURN(i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH);
+            ASSERT_THROW(j < sil.intersections.size());
+            ASSERT_THROW((j & 1) == 1);
+            ASSERT_THROW(sil.intersections[j].type == SegmentIntersection::OUTER_HIGH);
+            ASSERT_THROW(i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH);
             i = j + 1;
         }
     }
-#undef ASSERT_OR_RETURN
+#undef ASSERT_THROW
+
+    return segs;
+}
+
+// Connect each contour / vertical line intersection point with another two contour / vertical line intersection points.
+// (fill in SegmentIntersection::{prev_on_contour, prev_on_contour_vertical, next_on_contour, next_on_contour_vertical}.
+// These contour points are either on the same vertical line, or on the vertical line left / right to the current one.
+static void connect_segment_intersections_by_contours(const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs)
+{
+    for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
+	    SegmentedIntersectionLine       &il      = segs[i_vline];
+	    const SegmentedIntersectionLine *il_prev = i_vline > 0 ? &segs[i_vline - 1] : nullptr;
+	    const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr;
+
+        for (size_t i_intersection = 0; i_intersection + 1 < il.intersections.size(); ++ i_intersection) {
+		    SegmentIntersection &itsct = il.intersections[i_intersection];
+	        const Polygon 		&poly  = poly_with_offset.contour(itsct.iContour);
+
+	        // 1) Find possible connection points on the previous / next vertical line.
+		    // Find an intersection point on iVerticalLineOther, intersecting iInnerContour
+		    // at the same orientation as iIntersection, and being closest to iIntersection
+		    // in the number of contour segments, when following the direction of the contour.
+		    int iprev = -1;
+		    if (il_prev) {
+			    int dmin = std::numeric_limits<int>::max();
+			    for (size_t i = 0; i < il_prev->intersections.size(); ++ i) {
+			        const SegmentIntersection &itsct2 = il_prev->intersections[i];
+			        if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
+			            // The intersection points lie on the same contour and have the same orientation.
+			            // Find the intersection point with a shortest path in the direction of the contour.
+			            int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, false);
+			            if (d < dmin) {
+			                iprev = i;
+			                dmin  = d;
+			            }
+			        }
+			    }
+			}
+		    int inext = -1;
+		    if (il_next) {
+			    int dmin = std::numeric_limits<int>::max();
+			    for (size_t i = 0; i < il_next->intersections.size(); ++ i) {
+			        const SegmentIntersection &itsct2 = il_next->intersections[i];
+			        if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
+			            // The intersection points lie on the same contour and have the same orientation.
+			            // Find the intersection point with a shortest path in the direction of the contour.
+			            int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, true);
+			            if (d < dmin) {
+			                inext = i;
+			                dmin  = d;
+			            }
+			        }
+			    }
+			}
+
+	        // 2) Find possible connection points on the same vertical line.
+	        int iabove = -1;
+            // Does the perimeter intersect the current vertical line above intrsctn?
+            for (size_t i = i_intersection + 1; i + 1 < il.intersections.size(); ++ i)
+                if (il.intersections[i].iContour == itsct.iContour) {
+                    iabove = i;
+                    break;
+                }
+            // Does the perimeter intersect the current vertical line below intrsctn?
+	        int ibelow = -1;
+            for (size_t i = i_intersection - 1; i > 0; -- i)
+                if (il.intersections[i].iContour == itsct.iContour) {
+                    ibelow = i;
+                    break;
+                }
+
+	        // 3) Sort the intersection points, clear iprev / inext / iSegBelow / iSegAbove,
+	        // if it is preceded by any other intersection point along the contour.
+	        // The perimeter contour orientation.
+	        const bool forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour);
+	        {
+	            int d_horiz = (iprev  == -1) ? std::numeric_limits<int>::max() :
+	                distance_of_segmens(poly, il_prev->intersections[iprev].iSegment, itsct.iSegment, forward);
+	            int d_down  = (ibelow == -1) ? std::numeric_limits<int>::max() :
+	                distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward);
+	            int d_up    = (iabove == -1) ? std::numeric_limits<int>::max() :
+	                distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward);
+	            if (d_horiz < std::min(d_down, d_up)) {
+                    itsct.prev_on_contour 	    = iprev;
+                    itsct.prev_on_contour_type  = SegmentIntersection::LinkType::Horizontal;
+	            } else if (d_down < d_up) {
+                    itsct.prev_on_contour 		= ibelow;
+                    itsct.prev_on_contour_type  = SegmentIntersection::LinkType::Down;
+                } else {
+                    itsct.prev_on_contour       = iabove;
+                    itsct.prev_on_contour_type  = SegmentIntersection::LinkType::Up;
+                }
+	            // There should always be a link to the next intersection point on the same contour.
+	            assert(itsct.prev_on_contour != -1);
+	        }
+	        {
+	            int d_horiz = (inext  == -1) ? std::numeric_limits<int>::max() :
+	                distance_of_segmens(poly, itsct.iSegment, il_next->intersections[inext].iSegment, forward);
+	            int d_down  = (ibelow == -1) ? std::numeric_limits<int>::max() :
+	                distance_of_segmens(poly, itsct.iSegment, il.intersections[ibelow].iSegment, forward);
+	            int d_up    = (iabove == -1) ? std::numeric_limits<int>::max() :
+	                distance_of_segmens(poly, itsct.iSegment, il.intersections[iabove].iSegment, forward);
+	            if (d_horiz < std::min(d_down, d_up)) {
+                    itsct.next_on_contour 	    = inext;
+                    itsct.next_on_contour_type  = SegmentIntersection::LinkType::Horizontal;
+                } else if (d_down < d_up) {
+                    itsct.next_on_contour       = ibelow;
+                    itsct.next_on_contour_type  = SegmentIntersection::LinkType::Down;
+                } else {
+                    itsct.next_on_contour       = iabove;
+                    itsct.next_on_contour_type  = SegmentIntersection::LinkType::Up;
+                }
+	            // There should always be a link to the next intersection point on the same contour.
+	            assert(itsct.next_on_contour != -1);
+	        }
+	    }
+    }
+}
+
+// Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection.
+// Such intersection shall always exist.
+static const SegmentIntersection& end_of_vertical_run_raw(const SegmentIntersection &start)
+{
+	assert(start.type != SegmentIntersection::INNER_LOW);
+    // Step back to the beginning of the vertical segment to mark it as consumed.
+    auto *it = &start;
+    do {
+        ++ it;
+    } while (it->type != SegmentIntersection::OUTER_HIGH);
+    if ((it - 1)->is_inner()) {
+        // Step back.
+        -- it;
+        assert(it->type == SegmentIntersection::INNER_HIGH);
+    }
+    return *it;
+}
+static SegmentIntersection& end_of_vertical_run_raw(SegmentIntersection &start)
+{
+	return const_cast<SegmentIntersection&>(end_of_vertical_run_raw(std::as_const(start)));
+}
+
+// Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection, traversing vertical up contours if enabled.
+// Such intersection shall always exist.
+static const SegmentIntersection& end_of_vertical_run(const SegmentedIntersectionLine &il, const SegmentIntersection &start)
+{
+	assert(start.type != SegmentIntersection::INNER_LOW);
+	const SegmentIntersection *end = &end_of_vertical_run_raw(start);
+	assert(end->type == SegmentIntersection::INNER_HIGH);
+	for (;;) {
+		int up = end->vertical_up();
+		if (up == -1 || (end->has_left_vertical_up() ? end->prev_on_contour_quality : end->next_on_contour_quality) != SegmentIntersection::LinkQuality::Valid)
+			break;
+		const SegmentIntersection &new_start = il.intersections[up];
+		assert(end->iContour == new_start.iContour);
+		assert(new_start.type == SegmentIntersection::INNER_LOW);
+		end = &end_of_vertical_run_raw(new_start);
+	}
+	assert(end->type == SegmentIntersection::INNER_HIGH);
+	return *end;
+}
+static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, SegmentIntersection &start)
+{
+	return const_cast<SegmentIntersection&>(end_of_vertical_run(std::as_const(il), std::as_const(start)));
+}
+
+static void classify_vertical_runs(
+	const ExPolygonWithOffset &poly_with_offset, const FillParams &params, const coord_t link_max_length, 
+	std::vector<SegmentedIntersectionLine> &segs, size_t i_vline)
+{
+	SegmentedIntersectionLine &vline = segs[i_vline];
+    for (size_t i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) {
+    	if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) {
+    		if (vline.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) {
+    			for (;;) {
+        			SegmentIntersection &start = vline.intersections[i_intersection];
+			        SegmentIntersection &end   = end_of_vertical_run_raw(start);
+			        SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid;
+					// End of a contour starting at end and ending above end at the same vertical line.
+					int inext = end.vertical_outside();
+					if (inext == -1) {
+						i_intersection = &end - vline.intersections.data() + 1;
+						break;
+					}
+        			SegmentIntersection &start2 = vline.intersections[inext];
+        			if (params.dont_connect)
+        				link_quality = SegmentIntersection::LinkQuality::TooLong;
+        			else {
+                        for (SegmentIntersection *it = &end + 1; it != &start2; ++ it)
+                            if (it->is_inner()) {
+		        				link_quality = SegmentIntersection::LinkQuality::Invalid;
+                                break;
+                            }
+        				if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) {
+                        	// Measure length of the link.
+	                        coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
+	                            poly_with_offset, segs, i_vline, i_intersection, inext, end.has_right_vertical_outside());
+	                        if (link_length > link_max_length)
+		        				link_quality = SegmentIntersection::LinkQuality::TooLong;
+                        }
+                    }
+                    (end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality;
+                    (start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality;
+                    if (link_quality != SegmentIntersection::LinkQuality::Valid) {
+						i_intersection = &end - vline.intersections.data() + 1;
+                    	break;
+                    }
+					i_intersection = &start2 - vline.intersections.data();
+                }
+    		} else
+    			++ i_intersection;
+    	} else
+    		++ i_intersection;
+    }	
+}
+
+static void classify_horizontal_links(
+	const ExPolygonWithOffset &poly_with_offset, const FillParams &params, const coord_t link_max_length, 
+	std::vector<SegmentedIntersectionLine> &segs, size_t i_vline)
+{
+	SegmentedIntersectionLine &vline_left  = segs[i_vline];
+	SegmentedIntersectionLine &vline_right = segs[i_vline + 1];
+
+	// Traverse both left and right together.
+	size_t i_intersection_left  = 0;
+	size_t i_intersection_right = 0;
+	while (i_intersection_left + 1 < vline_left.intersections.size() && i_intersection_right + 1 < vline_right.intersections.size()) {
+    	if (i_intersection_left < vline_left.intersections.size() && vline_left.intersections[i_intersection_left].type != SegmentIntersection::INNER_LOW) {
+    		++ i_intersection_left;
+    		continue;
+    	}
+    	if (i_intersection_right < vline_right.intersections.size() && vline_right.intersections[i_intersection_right].type != SegmentIntersection::INNER_LOW) {
+    		++ i_intersection_right;
+    		continue;
+    	}
+
+		if (i_intersection_left + 1 >= vline_left.intersections.size()) {
+			// Trace right only.
+		} else if (i_intersection_right + 1 >= vline_right.intersections.size()) {
+			// Trace left only.
+		} else {
+			// Trace both.
+			SegmentIntersection &start_left  = vline_left.intersections[i_intersection_left];
+	        SegmentIntersection &end_left    = end_of_vertical_run(vline_left, start_left);
+			SegmentIntersection &start_right = vline_right.intersections[i_intersection_right];
+	        SegmentIntersection &end_right   = end_of_vertical_run(vline_right, start_right);
+	        // Do these runs overlap?
+	        int                    end_right_horizontal = end_left.right_horizontal();
+	        int                    end_left_horizontal  = end_right.left_horizontal();
+	        if (end_right_horizontal != -1) {
+	        	if (end_right_horizontal < &start_right - vline_right.intersections.data()) {
+	        		// Left precedes the right segment.
+	        	}
+	        } else if (end_left_horizontal != -1) {
+	        	if (end_left_horizontal < &start_left - vline_left.intersections.data()) {
+	        		// Right precedes the left segment.
+	        	}
+	        }
+		}
+	}
+
+#if 0
+    for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) {
+    	if (segs.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) {
+    		if (segs.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) {
+    			for (;;) {
+        			SegmentIntersection &start = segs.intersections[i_intersection];
+			        SegmentIntersection &end   = end_of_vertical_run_raw(start);
+			        SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid;
+					// End of a contour starting at end and ending above end at the same vertical line.
+					int inext = end.vertical_outside();
+					if (inext == -1) {
+						i_intersection = &end - segs.intersections.data() + 1;
+						break;
+					}
+        			SegmentIntersection &start2 = segs.intersections[inext];
+        			if (params.dont_connect)
+        				link_quality = SegmentIntersection::LinkQuality::TooLong;
+        			else {
+                        for (SegmentIntersection *it = &end + 1; it != &start2; ++ it)
+                            if (it->is_inner()) {
+		        				link_quality = SegmentIntersection::LinkQuality::Invalid;
+                                break;
+                            }
+        				if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) {
+                        	// Measure length of the link.
+	                        coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
+	                            poly_with_offset, segs, i_vline, i_intersection, inext, intrsctn->has_right_vertical_outside());
+	                        if (link_length > link_max_length)
+		        				link_quality = SegmentIntersection::LinkQuality::TooLong;
+                        }
+                    }
+                    (end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality;
+                    (start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality;
+                    if (link_quality != SegmentIntersection::LinkQuality::Valid) {
+						i_intersection = &end - segs.intersections.data() + 1;
+                    	break;
+                    }
+					i_intersection = &start2 - segs.intersections.data();
+                }
+    		} else
+    			++ i_intersection;
+    	} else
+    		++ i_intersection;
+    }
+#endif
+}
+
+static void disconnect_invalid_contour_links(
+	const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs)
+{
+	// Make the links symmetric!
+
+	// Validate vertical runs including vertical contour links.
+    for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
+		classify_vertical_runs(poly_with_offset, params, link_max_length, segs, i_vline);
+		if (i_vline > 0)
+			classify_horizontal_links(poly_with_offset, params, link_max_length, segs, i_vline - 1);
+    }
+}
+
+static void traverse_graph_generate_polylines(
+	const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs, Polylines& polylines_out)
+{
+    // For each outer only chords, measure their maximum distance to the bow of the outer contour.
+    // Mark an outer only chord as consumed, if the distance is low.
+    for (size_t i_vline = 0; i_vline < segs.size(); ++i_vline) {
+        SegmentedIntersectionLine& seg = segs[i_vline];
+        for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++i_intersection) {
+            if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW &&
+                seg.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) {
+                bool consumed = false;
+                //                if (params.full_infill()) {
+                //                        measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection);
+                //                } else
+                consumed = true;
+                seg.intersections[i_intersection].consumed_vertical_up = consumed;
+            }
+        }
+    }
+
+    // Now construct a graph.
+    // Find the first point.
+    // Naively one would expect to achieve best results by chaining the paths by the shortest distance,
+    // but that procedure does not create the longest continuous paths.
+    // A simple "sweep left to right" procedure achieves better results.
+    size_t    i_vline = 0;
+    size_t    i_intersection = size_t(-1);
+    // Follow the line, connect the lines into a graph.
+    // Until no new line could be added to the output path:
+    Point     pointLast;
+    Polyline* polyline_current = NULL;
+    if (!polylines_out.empty())
+        pointLast = polylines_out.back().points.back();
+    for (;;) {
+        if (i_intersection == size_t(-1)) {
+            // The path has been interrupted. Find a next starting point, closest to the previous extruder position.
+            coordf_t dist2min = std::numeric_limits<coordf_t>().max();
+            for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++i_vline2) {
+                const SegmentedIntersectionLine& seg = segs[i_vline2];
+                if (!seg.intersections.empty()) {
+                    assert(seg.intersections.size() > 1);
+                    // Even number of intersections with the loops.
+                    assert((seg.intersections.size() & 1) == 0);
+                    assert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW);
+                    for (size_t i = 0; i < seg.intersections.size(); ++i) {
+                        const SegmentIntersection& intrsctn = seg.intersections[i];
+                        if (intrsctn.is_outer()) {
+                            assert(intrsctn.is_low() || i > 0);
+                            bool consumed = intrsctn.is_low() ?
+                                intrsctn.consumed_vertical_up :
+                                seg.intersections[i - 1].consumed_vertical_up;
+                            if (!consumed) {
+                                coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
+                                if (dist2 < dist2min) {
+                                    dist2min = dist2;
+                                    i_vline = i_vline2;
+                                    i_intersection = i;
+                                    //FIXME We are taking the first left point always. Verify, that the caller chains the paths
+                                    // by a shortest distance, while reversing the paths if needed.
+                                    //if (polylines_out.empty())
+                                        // Initial state, take the first line, which is the first from the left.
+                                    goto found;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (i_intersection == size_t(-1))
+                // We are finished.
+                break;
+        found:
+            // Start a new path.
+            polylines_out.push_back(Polyline());
+            polyline_current = &polylines_out.back();
+            // Emit the first point of a path.
+            pointLast = Point(segs[i_vline].pos, segs[i_vline].intersections[i_intersection].pos());
+            polyline_current->points.push_back(pointLast);
+        }
+
+        // From the initial point (i_vline, i_intersection), follow a path.
+        SegmentedIntersectionLine& seg = segs[i_vline];
+        SegmentIntersection* intrsctn = &seg.intersections[i_intersection];
+        bool going_up = intrsctn->is_low();
+        bool try_connect = false;
+        if (going_up) {
+            assert(!intrsctn->consumed_vertical_up);
+            assert(i_intersection + 1 < seg.intersections.size());
+            // Step back to the beginning of the vertical segment to mark it as consumed.
+            if (intrsctn->is_inner()) {
+                assert(i_intersection > 0);
+                --intrsctn;
+                --i_intersection;
+            }
+            // Consume the complete vertical segment up to the outer contour.
+            do {
+                intrsctn->consumed_vertical_up = true;
+                ++intrsctn;
+                ++i_intersection;
+                assert(i_intersection < seg.intersections.size());
+            } while (intrsctn->type != SegmentIntersection::OUTER_HIGH);
+            if ((intrsctn - 1)->is_inner()) {
+                // Step back.
+                --intrsctn;
+                --i_intersection;
+                assert(intrsctn->type == SegmentIntersection::INNER_HIGH);
+                try_connect = true;
+            }
+        } else {
+            // Going down.
+            assert(intrsctn->is_high());
+            assert(i_intersection > 0);
+            assert(!(intrsctn - 1)->consumed_vertical_up);
+            // Consume the complete vertical segment up to the outer contour.
+            if (intrsctn->is_inner())
+                intrsctn->consumed_vertical_up = true;
+            do {
+                assert(i_intersection > 0);
+                --intrsctn;
+                --i_intersection;
+                intrsctn->consumed_vertical_up = true;
+            } while (intrsctn->type != SegmentIntersection::OUTER_LOW);
+            if ((intrsctn + 1)->is_inner()) {
+                // Step back.
+                ++intrsctn;
+                ++i_intersection;
+                assert(intrsctn->type == SegmentIntersection::INNER_LOW);
+                try_connect = true;
+            }
+        }
+        if (try_connect) {
+            // Decide, whether to finish the segment, or whether to follow the perimeter.
+
+            // 1) Find possible connection points on the previous / next vertical line.
+            IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection);
+            IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection);
+            // Try to connect to a previous or next vertical line, making a zig-zag pattern.
+            if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) {
+            	int iPrev = intrsctn->left_horizontal();
+            	int iNext = intrsctn->right_horizontal();
+                coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
+                    measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, iPrev);
+                coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
+                    measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, iNext);
+                // Take the shorter path.
+                //FIXME this may not be always the best strategy to take the shortest connection line now.
+                bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ?
+                    (distNext < distPrev) :
+                    intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK;
+                assert(intrsctn->is_inner());
+                bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length);
+                if (skip) {
+                    // Just skip the connecting contour and start a new path.
+                    goto dont_connect;
+                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
+                    polylines_out.push_back(Polyline());
+                    polyline_current = &polylines_out.back();
+                    const SegmentedIntersectionLine& il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)];
+                    polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos()));
+                } else {
+                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
+                    emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next);
+                }
+                // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
+                if (iPrev != -1)
+                    segs[i_vline - 1].intersections[iPrev].consumed_perimeter_right = true;
+                if (iNext != -1)
+                    intrsctn->consumed_perimeter_right = true;
+                //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed.
+                // Advance to the neighbor line.
+                if (take_next) {
+                    ++i_vline;
+                    i_intersection = iNext;
+                }
+                else {
+                    --i_vline;
+                    i_intersection = iPrev;
+                }
+                continue;
+            }
+
+            // 5) Try to connect to a previous or next point on the same vertical line.
+            if (int inext = intrsctn->vertical_outside(); inext != -1) {
+                bool valid = true;
+                // Verify, that there is no intersection with the inner contour up to the end of the contour segment.
+                // Verify, that the successive segment has not been consumed yet.
+                if (going_up) {
+                    if (seg.intersections[inext].consumed_vertical_up)
+                        valid = false;
+                    else {
+                        for (int i = (int)i_intersection + 1; i < inext && valid; ++i)
+                            if (seg.intersections[i].is_inner())
+                                valid = false;
+                    }
+                } else {
+                    if (seg.intersections[inext - 1].consumed_vertical_up)
+                        valid = false;
+                    else {
+                        for (int i = inext + 1; i < (int)i_intersection && valid; ++i)
+                            if (seg.intersections[i].is_inner())
+                                valid = false;
+                    }
+                }
+                if (valid) {
+                    const Polygon& poly = poly_with_offset.contour(intrsctn->iContour);
+                    assert(intrsctn->iContour == seg.intersections[inext].iContour);
+                    int iSegNext = seg.intersections[inext].iSegment;
+                    // Skip this perimeter line?
+                    bool skip = params.dont_connect;
+                    bool dir_forward = intrsctn->has_right_vertical_outside();
+                    if (! skip && link_max_length > 0) {
+                        coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
+                            poly_with_offset, segs, i_vline, i_intersection, inext, dir_forward);
+                        skip = link_length > link_max_length;
+                    }
+                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
+                    if (skip) {
+                        // Just skip the connecting contour and start a new path.
+                        polylines_out.push_back(Polyline());
+                        polyline_current = &polylines_out.back();
+                        polyline_current->points.push_back(Point(seg.pos, seg.intersections[inext].pos()));
+                    } else {
+                        // Consume the connecting contour and the next segment.
+                        emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline_current, dir_forward);
+                    }
+                    // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
+                    // If there are any outer intersection points skipped (bypassed) by the contour,
+                    // mark them as processed.
+                    if (going_up) {
+                        for (int i = (int)i_intersection; i < inext; ++i)
+                            seg.intersections[i].consumed_vertical_up = true;
+                    } else {
+                        for (int i = inext; i < (int)i_intersection; ++i)
+                            seg.intersections[i].consumed_vertical_up = true;
+                    }
+                    // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true;
+                    intrsctn->consumed_perimeter_right = true;
+                    i_intersection = inext;
+                    if (going_up)
+                        ++intrsctn;
+                    else
+                        --intrsctn;
+                    intrsctn->consumed_perimeter_right = true;
+                    continue;
+                }
+            }
+        dont_connect:
+            // No way to continue the current polyline. Take the rest of the line up to the outer contour.
+            // This will finish the polyline, starting another polyline at a new point.
+            if (going_up)
+                ++intrsctn;
+            else
+                --intrsctn;
+        }
+
+        // Finish the current vertical line,
+        // reset the current vertical line to pick a new starting point in the next round.
+        assert(intrsctn->is_outer());
+        assert(intrsctn->is_high() == going_up);
+        pointLast = Point(seg.pos, intrsctn->pos());
+        polyline_current->points.push_back(pointLast);
+        // Handle duplicate points and zero length segments.
+        polyline_current->remove_duplicate_points();
+        assert(!polyline_current->has_duplicate_points());
+        // Handle nearly zero length edges.
+        if (polyline_current->points.size() <= 1 ||
+            (polyline_current->points.size() == 2 &&
+                std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON &&
+                std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON))
+            polylines_out.pop_back();
+        intrsctn = NULL;
+        i_intersection = -1;
+        polyline_current = NULL;
+    }
+}
+
+struct MonotonousRegion;
+
+struct NextMonotonousRegion
+{
+	MonotonousRegion *region;
+	struct Path {
+		float length { 0 }; 		// Length of the link to the next region.
+		float visibility { 0 }; 	// 1 / length. Which length, just to the next region, or including the path accross the region?
+		float pheromone { 0 }; 		// <0, 1>
+	};
+	enum Index : int {
+		LowLow,
+		LowHigh,
+		HighLow,
+		HighHigh
+	};
+	Path paths[4];
+};
+
+struct MonotonousRegion
+{
+    struct Boundary {
+        int vline;
+        int low;
+        int high;
+    };
+
+    Boundary 	left;
+    Boundary 	right;
+
+    // Length when starting at left.low
+    double 		len1;
+    // Length when starting at left.high
+    double 		len2;
+    // If true, then when starting at left.low, then ending at right.high and vice versa.
+    // If false, then ending at the same side as starting.
+    bool 		flips;
+
+    int 		left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; }
+    int 		right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; }
+
+    // Left regions are used to track whether all regions left to this one have already been printed.
+    boost::container::small_vector<MonotonousRegion*, 4>	left_neighbors;
+    // Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics.
+    boost::container::small_vector<NextMonotonousRegion, 4>	right_neighbors;
+};
+
+struct MonotonousRegionLink
+{
+    MonotonousRegion    *region;
+    bool 				 flipped;
+    // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region 
+    // is applied as defined.
+    NextMonotonousRegion::Path *next;
+    // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region
+    // is applied in reverse order as if the zig-zags were flipped.
+    NextMonotonousRegion::Path *next_flipped;
+};
+
+static const SegmentIntersection& vertical_run_bottom(const SegmentedIntersectionLine &vline, const SegmentIntersection &start)
+{
+	assert(start.is_inner());
+	const SegmentIntersection *it = &start;
+	// Find the lowest SegmentIntersection::INNER_LOW starting with right.
+	for (;;) {
+		while (it->type != SegmentIntersection::INNER_LOW)
+			-- it;
+		int down = it->vertical_down();
+		if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid)
+			break;
+		it = &vline.intersections[down];
+	}
+	return *it;
+}
+static SegmentIntersection& vertical_run_bottom(SegmentedIntersectionLine& vline, SegmentIntersection& start)
+{
+    return const_cast<SegmentIntersection&>(vertical_run_bottom(std::as_const(vline), std::as_const(start)));
+}
+
+static const SegmentIntersection& vertical_run_top(const SegmentedIntersectionLine &vline, const SegmentIntersection &start)
+{
+	assert(start.is_inner());
+	const SegmentIntersection *it = &start;
+	// Find the lowest SegmentIntersection::INNER_LOW starting with right.
+	for (;;) {
+		while (it->type != SegmentIntersection::INNER_HIGH)
+			++ it;
+		int up = it->vertical_up();
+		if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid)
+			break;
+		it = &vline.intersections[up];
+	}
+	return *it;
+}
+static SegmentIntersection& vertical_run_top(SegmentedIntersectionLine& vline, SegmentIntersection& start)
+{
+    return const_cast<SegmentIntersection&>(vertical_run_top(std::as_const(vline), std::as_const(start)));
+}
+
+static SegmentIntersection* left_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
+{
+	SegmentIntersection *left = nullptr;
+	for (SegmentIntersection *it = &start; it <= &end; ++ it) {
+		int i = it->left_horizontal();
+		if (i != -1) {
+			left = &vline_left.intersections[i];
+			break;
+		}
+	}
+	return left == nullptr ? nullptr : &vertical_run_bottom(vline_left, *left);
+}
+
+static SegmentIntersection* left_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
+{
+	SegmentIntersection *left = nullptr;
+	for (SegmentIntersection *it = &end; it >= &start; -- it) {
+		int i = it->left_horizontal();
+		if (i != -1) {
+			left = &vline_left.intersections[i];
+			break;
+		}
+	}
+	return left == nullptr ? nullptr : &vertical_run_top(vline_left, *left);
+}
+
+static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
+{
+	std::pair<SegmentIntersection*, SegmentIntersection*> out(nullptr, nullptr);
+	out.first = left_overlap_bottom(start, end, vline_left);
+	if (out.first != nullptr)
+		out.second = left_overlap_top(start, end, vline_left);
+	return out;
+}
+
+static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_left)
+{
+	assert((start_end.first == nullptr) == (start_end.second == nullptr));
+	return start_end.first == nullptr ? start_end : left_overlap(*start_end.first, *start_end.second, vline_left);
+}
+
+static SegmentIntersection* right_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
+{
+	SegmentIntersection *right = nullptr;
+	for (SegmentIntersection *it = &start; it <= &end; ++ it) {
+		int i = it->right_horizontal();
+		if (i != -1) {
+			right = &vline_right.intersections[i];
+			break;
+		}
+	}
+	return right == nullptr ? nullptr : &vertical_run_bottom(vline_right, *right);
+}
+
+static SegmentIntersection* right_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
+{
+	SegmentIntersection *right = nullptr;
+	for (SegmentIntersection *it = &end; it >= &start; -- it) {
+		int i = it->right_horizontal();
+		if (i != -1) {
+			right = &vline_right.intersections[i];
+			break;
+		}
+	}
+	return right == nullptr ? nullptr : &vertical_run_top(vline_right, *right);
+}
+
+static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
+{
+	std::pair<SegmentIntersection*, SegmentIntersection*> out(nullptr, nullptr);
+	out.first = right_overlap_bottom(start, end, vline_right);
+	if (out.first != nullptr)
+		out.second = right_overlap_top(start, end, vline_right);
+	return out;
+}
+
+static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_right)
+{
+	assert((start_end.first == nullptr) == (start_end.second == nullptr));
+	return start_end.first == nullptr ? start_end : right_overlap(*start_end.first, *start_end.second, vline_right);
+}
+
+static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<SegmentedIntersectionLine> &segs)
+{
+	std::vector<MonotonousRegion> monotonous_regions;
+
+    for (size_t i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) {
+        SegmentedIntersectionLine  &vline_seed = segs[i_vline_seed];
+    	for (size_t i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) {
+	        while (i_intersection_seed + 1 < vline_seed.intersections.size() &&
+	        	   vline_seed.intersections[i_intersection_seed].type != SegmentIntersection::INNER_LOW)
+	        	++ i_intersection_seed;
+			SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed];
+            SegmentIntersection *end   = &end_of_vertical_run_raw(*start);
+			if (! start->consumed_vertical_up) {
+				// Draw a new monotonous region starting with this segment.
+				// while there is only a single right neighbor
+				start->consumed_vertical_up = true;
+		        size_t i_vline = i_vline_seed;
+                std::pair<SegmentIntersection*, SegmentIntersection*> left(start, end);
+				MonotonousRegion region;
+				region.left.vline = i_vline;
+				region.left.low   = left.first  - vline_seed.intersections.data();
+				region.left.high  = left.second - vline_seed.intersections.data();
+				region.right      = region.left;
+				while (++ i_vline < segs.size()) {
+			        SegmentedIntersectionLine  &vline_left	= segs[i_vline - 1];
+			        SegmentedIntersectionLine  &vline_right = segs[i_vline];
+					std::pair<SegmentIntersection*, SegmentIntersection*> right 	  = right_overlap(left, vline_right);
+					std::pair<SegmentIntersection*, SegmentIntersection*> right_left  = left_overlap(right, vline_left);
+					if (left != right_left)
+						// Left & right draws don't overlap exclusively.
+						break;
+					region.right.vline = i_vline;
+					region.right.low   = right.first  - vline_right.intersections.data();
+					region.right.high  = right.second - vline_right.intersections.data();
+					right.first->consumed_vertical_up = true;
+					left = right;
+				}
+			}
+			i_intersection_seed = end - vline_seed.intersections.data() + 1;
+		}
+    }
+
+    return monotonous_regions;
+}
+
+static void connect_monotonous_regions(std::vector<MonotonousRegion> &regions, std::vector<SegmentedIntersectionLine> &segs)
+{
+	// Map from low intersection to left / right side of a monotonous region.
+	using MapType = std::pair<SegmentIntersection*, MonotonousRegion*>;
+	std::vector<MapType> map_intersection_to_region_start;
+	std::vector<MapType> map_intersection_to_region_end;
+	map_intersection_to_region_start.reserve(regions.size());
+	map_intersection_to_region_end.reserve(regions.size());
+	for (MonotonousRegion &region : regions) {
+		map_intersection_to_region_start.emplace_back(&segs[region.left.vline].intersections[region.left.low], &region);
+		map_intersection_to_region_end.emplace_back(&segs[region.right.vline].intersections[region.right.low], &region);
+	}
+	auto intersections_lower = [](const MapType &l, const MapType &r){ return l.first < r.first ; };
+	auto intersections_equal = [](const MapType &l, const MapType &r){ return l.first == r.first ; };
+	std::sort(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), intersections_lower);
+	std::sort(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), intersections_lower);
+
+	// Scatter links to neighboring regions.
+	for (MonotonousRegion &region : regions) {
+		if (region.left.vline > 0) {
+			auto &vline = segs[region.left.vline];
+			auto  begin = &vline.intersections[region.left.low];
+			auto  end   = &vline.intersections[region.left.high];
+            for (;;) {
+                MapType key(begin, nullptr);
+                auto it = std::lower_bound(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), key);
+                assert(it != map_intersection_to_region_end.end() && it->first == key.first);
+                NextMonotonousRegion next_region{ &region };
+				it->second->right_neighbors.emplace_back(next_region);
+				SegmentIntersection *next = &vertical_run_top(vline, *begin);
+				if (next == end)
+					break;
+				while (next->type != SegmentIntersection::INNER_LOW)
+					++ next;
+				begin = next;
+			}
+		}
+		if (region.right.vline + 1 < segs.size()) {
+			auto &vline = segs[region.right.vline];
+			auto  begin = &vline.intersections[region.right.low];
+			auto  end   = &vline.intersections[region.right.high];
+			for (;;) {
+				MapType key(begin, nullptr);
+				auto it = std::lower_bound(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), key);
+				assert(it != map_intersection_to_region_start.end() && it->first == key.first);
+				it->second->left_neighbors.emplace_back(&region);
+				SegmentIntersection *next = &vertical_run_top(vline, *begin);
+				if (next == end)
+					break;
+				while (next->type != SegmentIntersection::INNER_LOW)
+					++ next;
+				begin = next;
+			}
+		}
+	}
+}
+
+// Raad Salman: Algorithms for the Precedence Constrained Generalized Travelling Salesperson Problem
+// https://www.chalmers.se/en/departments/math/research/research-groups/optimization/OptimizationMasterTheses/MScThesis-RaadSalman-final.pdf
+// Algorithm 6.1 Lexicographic Path Preserving 3-opt
+// Optimize path while maintaining the ordering constraints.
+void monotonous_3_opt(std::vector<MonotonousRegionLink> &path, std::vector<SegmentedIntersectionLine> &segs)
+{
+	// When doing the 3-opt path preserving flips, one has to fulfill two constraints:
+	//
+	// 1) The new path should be shorter than the old path.
+	// 2) The precedence constraints shall be satisified on the new path.
+	//
+	// Branch & bound with KD-tree may be used with the shorter path constraint, but the precedence constraint will have to be recalculated for each
+	// shorter path candidate found, which has a quadratic cost for a dense precedence graph. For a sparse precedence graph the precedence
+	// constraint verification will be cheaper.
+	//
+	// On the other side, if the full search space is traversed as in the diploma thesis by Raad Salman (page 24, Algorithm 6.1 Lexicographic Path Preserving 3-opt),
+	// then the precedence constraint verification is amortized inside the O(n^3) loop. Now which is better for our task?
+	//
+	// It is beneficial to also try flipping of the infill zig-zags, for which a prefix sum of both flipped and non-flipped paths over
+	// MonotonousRegionLinks may be utilized, however updating the prefix sum has a linear complexity, the same complexity as doing the 3-opt
+	// exchange by copying the pieces.
+}
+
+// Find a run through monotonous infill blocks using an 'Ant colony" optimization method.
+static std::vector<MonotonousRegionLink> chain_monotonous_regions(
+	std::vector<MonotonousRegion> &regions, std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
+{
+	// Start point of a region (left) given the direction of the initial infill line.
+	auto region_start_point = [&segs](const MonotonousRegion &region, bool dir) {
+		SegmentedIntersectionLine 	&vline  = segs[region.left.vline];
+		SegmentIntersection      	&ipt    = vline.intersections[dir ? region.left.high : region.left.low];
+		return Vec2f(float(vline.pos), float(ipt.pos()));
+	};
+	// End point of a region (right) given the direction of the initial infill line and whether the monotonous run contains
+	// even or odd number of vertical lines.
+    auto region_end_point = [&segs](const MonotonousRegion &region, bool dir) {
+		SegmentedIntersectionLine 	&vline  = segs[region.right.vline];
+		SegmentIntersection      	&ipt    = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high];
+		return Vec2f(float(vline.pos), float(ipt.pos()));
+	};
+
+	// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed).
+	std::vector<int32_t>			left_neighbors_unprocessed(regions.size(), 0);
+	// Queue of regions, which have their left neighbors already printed.
+	std::vector<MonotonousRegion*> 	queue;
+	queue.reserve(regions.size());
+	for (MonotonousRegion &region : regions)
+		if (region.left_neighbors.empty())
+			queue.emplace_back(&region);
+		else
+			left_neighbors_unprocessed[&region - regions.data()] = region.left_neighbors.size();
+	// Make copy of structures that need to be initialized at each ant iteration.
+	auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed;
+	auto queue_initial 						= queue;
+
+	std::vector<MonotonousRegionLink> path, best_path;
+	path.reserve(regions.size());
+	best_path.reserve(regions.size());
+	float best_path_length = std::numeric_limits<float>::max();
+
+	struct NextCandidate {
+        NextMonotonousRegion        *region;
+        NextMonotonousRegion::Path  *link;
+        NextMonotonousRegion::Path  *link_flipped;
+        float                        cost;
+		bool 			             dir;
+	};
+	std::vector<NextCandidate> next_candidates;
+
+	// How many times to repeat the ant simulation.
+	constexpr int num_runs = 10;
+	// With how many ants each of the run will be performed?
+	constexpr int num_ants = 10;
+	// Base (initial) pheromone level.
+	constexpr float pheromone_initial_deposit = 0.5f;
+	// Evaporation rate of pheromones.
+	constexpr float pheromone_evaporation = 0.1f;
+	// Probability at which to take the next best path. Otherwise take the the path based on the cost distribution.
+	constexpr float probability_take_best = 0.9f;
+	// Exponents of the cost function.
+	constexpr float pheromone_alpha = 1.f; // pheromone exponent
+	constexpr float pheromone_beta  = 2.f; // attractiveness weighted towards edge length
+	// Cost of traversing a link between two monotonous regions.
+	auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path &path) {
+		return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
+	};
+	for (int run = 0; run < num_runs; ++ run)
+	{
+		for (int ant = 0; ant < num_ants; ++ ant) 
+		{
+			// Find a new path following the pheromones deposited by the previous ants.
+			path.clear();
+			queue = queue_initial;
+			left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
+			while (! queue.empty()) {
+				// Sort the queue by distance to the last point.
+				// Take a candidate based on shortest distance? or ant colony?
+				if (path.empty()) {
+					// Pick randomly the first from the queue at random orientation.
+					int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng);
+                    path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 });
+					*(queue.begin() + first_idx) = std::move(queue.back());
+					queue.pop_back();
+				} else {
+					// Pick the closest neighbor from the queue?
+				}
+				-- left_neighbors_unprocessed[path.back().region - regions.data()];
+				while (! path.back().region->right_neighbors.empty()) {
+					// Chain.
+					MonotonousRegion 		    &region = *path.back().region;
+					bool 			  			 dir    = path.back().flipped;
+					Vec2f                    	 end_pt	= region_end_point(region, dir);
+					// Sort by distance to pt.
+					next_candidates.reserve(region.right_neighbors.size() * 2);
+					for (NextMonotonousRegion &next : region.right_neighbors) {
+						int unprocessed = left_neighbors_unprocessed[next.region - regions.data()];
+						assert(unprocessed > 0);
+						if (unprocessed == 1) {
+							// Dependencies of the successive blocks are satisfied.
+                            bool flip = dir == region.flips;
+                            auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path& path) {
+                                return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
+                            };
+                            NextMonotonousRegion::Path &path_low  		  = next.paths[flip ? NextMonotonousRegion::HighLow  : NextMonotonousRegion::LowLow];
+                            NextMonotonousRegion::Path &path_low_flipped  = next.paths[flip ? NextMonotonousRegion::LowHigh  : NextMonotonousRegion::HighHigh];
+                            NextMonotonousRegion::Path &path_high 	      = next.paths[flip ? NextMonotonousRegion::HighHigh : NextMonotonousRegion::LowHigh];
+                            NextMonotonousRegion::Path &path_high_flipped = next.paths[flip ? NextMonotonousRegion::LowLow   : NextMonotonousRegion::HighLow];
+                            next_candidates.emplace_back(NextCandidate{ &next, &path_low,  &path_low_flipped,  path_cost(path_low),  false });
+                            next_candidates.emplace_back(NextCandidate{ &next, &path_high, &path_high_flipped, path_cost(path_high), true });
+						}
+					}
+					//std::sort(next_candidates.begin(), next_candidates.end(), [](const auto &l, const auto &r) { l.dist < r.dist; });
+					float dice = float(rng()) / float(rng.max());
+                    std::vector<NextCandidate>::iterator take_path;
+					if (dice < probability_take_best) {
+						// Take the lowest cost path.
+						take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; });
+					} else {
+						// Take the path based on the cost.
+                        // Calculate the total cost.
+                        float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; });
+						// Take a random path based on the cost.
+                        float cost_threshold = floor(float(rng()) * total_cost / float(rng.max()));
+                        take_path = next_candidates.end();
+                        -- take_path;
+                        for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it)
+                            if (cost_threshold -= it->cost <= 0.) {
+                                take_path = it;
+                                break;
+                            }
+					}
+					// Extend the path.
+					NextMonotonousRegion &next_region = *take_path->region;
+					bool        		  next_dir    = take_path->dir;
+                    path.back().next         = take_path->link;
+                    path.back().next_flipped = take_path->link_flipped;
+                    path.emplace_back(MonotonousRegionLink{ next_region.region, next_dir });
+					// Decrease the number of next block dependencies.
+					-- left_neighbors_unprocessed[next_region.region - regions.data()];
+					// Update pheromones along this link.
+					take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit;
+				}
+			}
+
+			// Perform 3-opt local optimization of the path.
+			monotonous_3_opt(path, segs);
+
+			// Measure path length.
+            float path_length = std::accumulate(path.begin(), path.end(), 0.f, [](const float l, const MonotonousRegionLink& r) { return l + r.next->length; });
+			// Save the shortest path.
+			if (path_length < best_path_length) {
+				best_path_length = path_length;
+				std::swap(best_path_length, path_length);
+			}
+		}
+
+		// Reinforce the path feromones with the best path.
+        float total_cost = best_path_length;
+		for (MonotonousRegionLink &link : path)
+            link.next->pheromone = (1.f - pheromone_evaporation) * link.next->pheromone + pheromone_evaporation / total_cost;
+	}
+
+	return best_path;
+}
+
+// Traverse path, produce polylines.
+static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, Polylines &polylines_out)
+{
+	Polyline *polyline = nullptr;
+	auto finish_polyline = [&polyline, &polylines_out]() {
+        polyline->remove_duplicate_points();
+        // Handle duplicate points and zero length segments.
+        assert(!polyline->has_duplicate_points());
+        // Handle nearly zero length edges.
+        if (polyline->points.size() <= 1 ||
+            (polyline->points.size() == 2 &&
+                std::abs(polyline->points.front()(0) - polyline->points.back()(0)) < SCALED_EPSILON &&
+                std::abs(polyline->points.front()(1) - polyline->points.back()(1)) < SCALED_EPSILON))
+            polylines_out.pop_back();
+    	polyline = nullptr;
+    };
+
+	for (const MonotonousRegionLink &path_segment : path) {
+		MonotonousRegion &region = *path_segment.region;
+		bool 			  dir    = path_segment.flipped;
+
+        // From the initial point (i_vline, i_intersection), follow a path.
+		int  i_intersection = region.left_intersection_point(dir);
+		int  i_vline 		= region.left.vline;
+
+        if (polyline != nullptr && &path_segment != path.data()) {
+        	// Connect previous path segment with the new one.
+        	const MonotonousRegionLink 	      &path_segment_prev  = *(&path_segment - 1);
+			const MonotonousRegion 		      &region_prev		  = *path_segment_prev.region;
+			bool 			  			       dir_prev 		  = path_segment_prev.flipped;
+			int                                i_vline_prev       = region_prev.right.vline;
+			const SegmentedIntersectionLine   &vline_prev         = segs[i_vline_prev];
+			int 		       				   i_intersection_prev = region_prev.right_intersection_point(dir_prev);
+			const SegmentIntersection         *ip_prev 			  = &vline_prev.intersections[i_intersection_prev];
+			bool 						       extended           = false;
+			if (i_vline_prev + 1 == i_vline) {
+				if (ip_prev->right_horizontal() == i_intersection && ip_prev->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
+		        	// Emit a horizontal connection contour.
+		            emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline_prev, ip_prev->iContour, i_intersection_prev, i_intersection, *polyline, true);
+		            extended = true;
+				}
+	        }
+	        if (! extended) {
+		        // Finish the current vertical line,
+                assert(ip_prev->is_inner());
+                ip_prev->is_low() ? -- ip_prev : ++ ip_prev;
+		        assert(ip_prev->is_outer());
+	        	polyline->points.back() = Point(vline_prev.pos, ip_prev->pos());
+				finish_polyline();
+			}
+        }
+
+		for (;;) {
+	        const SegmentedIntersectionLine &seg = segs[i_vline];
+            const SegmentIntersection       *intrsctn = &seg.intersections[i_intersection];
+            const bool                       going_up = intrsctn->is_low();
+            if (polyline == nullptr) {
+				polylines_out.emplace_back();
+	            polyline = &polylines_out.back();
+	            // Extend the infill line up to the outer contour.
+	        	polyline->points.emplace_back(seg.pos, (intrsctn + (going_up ? - 1 : 1))->pos());
+			} else
+				polyline->points.emplace_back(seg.pos, intrsctn->pos());
+
+			int iright = intrsctn->right_horizontal();
+	        if (going_up) {
+	            // Consume the complete vertical segment up to the inner contour.
+	            for (;;) {
+		            do {
+		                ++ intrsctn;
+						iright = std::max(iright, intrsctn->right_horizontal());
+		            } while (intrsctn->type != SegmentIntersection::INNER_HIGH);
+	                polyline->points.emplace_back(seg.pos, intrsctn->pos());
+		            int inext = intrsctn->vertical_up();
+		            if (inext == -1)
+		            	break;
+		            const Polygon &poly = poly_with_offset.contour(intrsctn->iContour);
+	                assert(intrsctn->iContour == seg.intersections[inext].iContour);
+	                emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline, intrsctn->has_right_vertical_up());
+	                intrsctn = seg.intersections.data() + inext;
+	            } 
+	        } else {
+	            // Going down.
+	            assert(intrsctn->is_high());
+	            assert(i_intersection > 0);
+	            for (;;) {
+		            do {
+		                -- intrsctn;
+		                if (int iright_new = intrsctn->right_horizontal(); iright_new != -1)
+		                	iright = iright_new;
+		            } while (intrsctn->type != SegmentIntersection::INNER_LOW);
+	                polyline->points.emplace_back(seg.pos, intrsctn->pos());
+		            int inext = intrsctn->vertical_down();
+		            if (inext == -1)
+		            	break;
+		            const Polygon &poly = poly_with_offset.contour(intrsctn->iContour);
+	                assert(intrsctn->iContour == seg.intersections[inext].iContour);
+	                emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, intrsctn->has_right_vertical_down());
+	                intrsctn = seg.intersections.data() + inext;
+	            } 
+	        }
+
+	        if (i_vline == region.right.vline)
+	        	break;
+
+	        int inext = intrsctn->right_horizontal();
+	        if (inext != -1 && intrsctn->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
+	        	// Emit a horizontal connection contour.
+	            emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, true);
+	            i_intersection = inext;
+	        } else {
+		        // Finish the current vertical line,
+	        	going_up ? ++ intrsctn : -- intrsctn;
+		        assert(intrsctn->is_outer());
+		        assert(intrsctn->is_high() == going_up);
+	        	polyline->points.back() = Point(seg.pos, intrsctn->pos());
+				finish_polyline();
+				if (inext == -1) {
+					// Find the end of the next overlapping vertical segment.
+			        const SegmentedIntersectionLine &vline_right = segs[i_vline + 1];
+                    const SegmentIntersection       *right       = going_up ? 
+                        &vertical_run_top(vline_right, vline_right.intersections[iright]) : &vertical_run_bottom(vline_right, vline_right.intersections[iright]);
+					i_intersection = right - vline_right.intersections.data();
+				} else
+		            i_intersection = inext;
+	        }
+
+	        ++ i_vline;
+	    }
+    }
+}
+
+bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams &params, float angleBase, float pattern_shift, Polylines &polylines_out)
+{
+    // At the end, only the new polylines will be rotated back.
+    size_t n_polylines_out_initial = polylines_out.size();
+
+    // Shrink the input polygon a bit first to not push the infill lines out of the perimeters.
+//    const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
+    const float INFILL_OVERLAP_OVER_SPACING = 0.45f;
+    assert(INFILL_OVERLAP_OVER_SPACING > 0 && INFILL_OVERLAP_OVER_SPACING < 0.5f);
+
+    // Rotate polygons so that we can work with vertical lines here
+    std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
+    rotate_vector.first += angleBase;
+
+    assert(params.density > 0.0001f && params.density <= 1.f);
+    coord_t line_spacing = coord_t(scale_(this->spacing) / params.density);
+
+    // On the polygons of poly_with_offset, the infill lines will be connected.
+    ExPolygonWithOffset poly_with_offset(
+        surface->expolygon, 
+        - rotate_vector.first, 
+        scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing),
+        scale_(this->overlap - 0.5 * this->spacing));
+    if (poly_with_offset.n_contours_inner == 0) {
+        // Not a single infill line fits.
+        //FIXME maybe one shall trigger the gap fill here?
+        return true;
+    }
+
+    BoundingBox bounding_box = poly_with_offset.bounding_box_src();
+
+    // define flow spacing according to requested density
+    if (params.full_infill() && !params.dont_adjust) {
+        line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
+        this->spacing = unscale<double>(line_spacing);
+    } else {
+        // extend bounding box so that our pattern will be aligned with other layers
+        // Transform the reference point to the rotated coordinate system.
+        Point refpt = rotate_vector.second.rotated(- rotate_vector.first);
+        // _align_to_grid will not work correctly with positive pattern_shift.
+        coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing;
+        refpt(0) -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
+        bounding_box.merge(_align_to_grid(
+            bounding_box.min, 
+            Point(line_spacing, line_spacing), 
+            refpt));
+    }
+
+    // Intersect a set of euqally spaced vertical lines wiht expolygon.
+    // n_vlines = ceil(bbox_width / line_spacing)
+    size_t  n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing;
+	coord_t x0 = bounding_box.min(0);
+	if (params.full_infill())
+		x0 += (line_spacing + SCALED_EPSILON) / 2;
+
+#ifdef SLIC3R_DEBUG
+    static int iRun = 0;
+    BoundingBox bbox_svg = poly_with_offset.bounding_box_outer();
+    ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.));
+    poly_with_offset.export_to_svg(svg);
+    {
+        ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-initial-%d.svg", iRun), bbox_svg); // , scale_(1.));
+        poly_with_offset.export_to_svg(svg);
+    }
+    iRun ++;
+#endif /* SLIC3R_DEBUG */
+
+    std::vector<SegmentedIntersectionLine> segs = slice_region_by_vertical_lines(poly_with_offset, n_vlines, x0, line_spacing);
+	connect_segment_intersections_by_contours(poly_with_offset, segs);
 
 #ifdef SLIC3R_DEBUG
     // Paint the segments and finalize the SVG file.
@@ -1018,352 +2224,15 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
     svg.Close();
 #endif /* SLIC3R_DEBUG */
 
-    // For each outer only chords, measure their maximum distance to the bow of the outer contour.
-    // Mark an outer only chord as consumed, if the distance is low.
-    for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
-        SegmentedIntersectionLine &seg = segs[i_vline];
-        for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) {
-            if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW &&
-                seg.intersections[i_intersection+1].type == SegmentIntersection::OUTER_HIGH) {
-                bool consumed = false;
-//                if (params.full_infill()) {
-//                        measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection);
-//                } else
-                    consumed = true;
-                seg.intersections[i_intersection].consumed_vertical_up = consumed;
-            }
-        }
-    }
-
-    // Now construct a graph.
-    // Find the first point.
-    // Naively one would expect to achieve best results by chaining the paths by the shortest distance,
-    // but that procedure does not create the longest continuous paths.
-    // A simple "sweep left to right" procedure achieves better results.
-    size_t    i_vline = 0;
-    size_t    i_intersection = size_t(-1);
-    // Follow the line, connect the lines into a graph.
-    // Until no new line could be added to the output path:
-    Point     pointLast;
-    Polyline *polyline_current = NULL;
-    if (! polylines_out.empty())
-        pointLast = polylines_out.back().points.back();
-    for (;;) {
-        if (i_intersection == size_t(-1)) {
-            // The path has been interrupted. Find a next starting point, closest to the previous extruder position.
-            coordf_t dist2min = std::numeric_limits<coordf_t>().max();
-            for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) {
-                const SegmentedIntersectionLine &seg = segs[i_vline2];
-                if (! seg.intersections.empty()) {
-                    assert(seg.intersections.size() > 1);
-                    // Even number of intersections with the loops.
-                    assert((seg.intersections.size() & 1) == 0);
-                    assert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW);
-                    for (size_t i = 0; i < seg.intersections.size(); ++ i) {
-                        const SegmentIntersection &intrsctn = seg.intersections[i];
-                        if (intrsctn.is_outer()) {
-                            assert(intrsctn.is_low() || i > 0);
-                            bool consumed = intrsctn.is_low() ? 
-                                intrsctn.consumed_vertical_up : 
-                                seg.intersections[i-1].consumed_vertical_up;
-                            if (! consumed) {
-                                coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
-                                if (dist2 < dist2min) {
-                                    dist2min = dist2;
-                                    i_vline = i_vline2;
-                                    i_intersection = i;
-                                    //FIXME We are taking the first left point always. Verify, that the caller chains the paths
-                                    // by a shortest distance, while reversing the paths if needed.
-                                    //if (polylines_out.empty())
-                                        // Initial state, take the first line, which is the first from the left.
-                                        goto found;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            if (i_intersection == size_t(-1))
-                // We are finished.
-                break;
-        found:
-            // Start a new path.
-            polylines_out.push_back(Polyline());
-            polyline_current = &polylines_out.back();
-            // Emit the first point of a path.
-            pointLast = Point(segs[i_vline].pos, segs[i_vline].intersections[i_intersection].pos());
-            polyline_current->points.push_back(pointLast);
-        }
-
-        // From the initial point (i_vline, i_intersection), follow a path.
-        SegmentedIntersectionLine &seg      = segs[i_vline];
-        SegmentIntersection       *intrsctn = &seg.intersections[i_intersection];
-        bool going_up = intrsctn->is_low();
-        bool try_connect = false;
-        if (going_up) {
-            assert(! intrsctn->consumed_vertical_up);
-            assert(i_intersection + 1 < seg.intersections.size());
-            // Step back to the beginning of the vertical segment to mark it as consumed.
-            if (intrsctn->is_inner()) {
-                assert(i_intersection > 0);
-                -- intrsctn;
-                -- i_intersection;
-            }
-            // Consume the complete vertical segment up to the outer contour.
-            do {
-                intrsctn->consumed_vertical_up = true;
-                ++ intrsctn;
-                ++ i_intersection;
-                assert(i_intersection < seg.intersections.size());
-            } while (intrsctn->type != SegmentIntersection::OUTER_HIGH);
-            if ((intrsctn - 1)->is_inner()) {
-                // Step back.
-                -- intrsctn;
-                -- i_intersection;
-                assert(intrsctn->type == SegmentIntersection::INNER_HIGH);
-                try_connect = true;
-            }
-        } else {
-            // Going down.
-            assert(intrsctn->is_high());
-            assert(i_intersection > 0);
-            assert(! (intrsctn - 1)->consumed_vertical_up);
-            // Consume the complete vertical segment up to the outer contour.
-            if (intrsctn->is_inner())
-                intrsctn->consumed_vertical_up = true;
-            do {
-                assert(i_intersection > 0);
-                -- intrsctn;
-                -- i_intersection;
-                intrsctn->consumed_vertical_up = true;
-            } while (intrsctn->type != SegmentIntersection::OUTER_LOW);
-            if ((intrsctn + 1)->is_inner()) {
-                // Step back.
-                ++ intrsctn;
-                ++ i_intersection;
-                assert(intrsctn->type == SegmentIntersection::INNER_LOW);
-                try_connect = true;
-            }
-        }
-        if (try_connect) {
-            // Decide, whether to finish the segment, or whether to follow the perimeter.
-
-            // 1) Find possible connection points on the previous / next vertical line.
-            int iPrev = intersection_on_prev_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection);
-            int iNext = intersection_on_next_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection);
-            IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection, iPrev);
-            IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection, iNext);
-
-            // 2) Find possible connection points on the same vertical line.
-            int iAbove = -1;
-            int iBelow = -1;
-            int iSegAbove = -1;
-            int iSegBelow = -1;
-            {
-//                SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
-//                    SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
-                // Does the perimeter intersect the current vertical line above intrsctn?
-                for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i)
-//                    if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) {
-                    if (seg.intersections[i].iContour == intrsctn->iContour) {
-                        iAbove = i;
-                        iSegAbove = seg.intersections[i].iSegment;
-                        break;
-                    }
-                // Does the perimeter intersect the current vertical line below intrsctn?
-                for (size_t i = i_intersection - 1; i > 0; -- i)
-//                    if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) {
-                    if (seg.intersections[i].iContour == intrsctn->iContour) {
-                        iBelow = i;
-                        iSegBelow = seg.intersections[i].iSegment;
-                        break;
-                    }
-            }
-
-            // 3) Sort the intersection points, clear iPrev / iNext / iSegBelow / iSegAbove,
-            // if it is preceded by any other intersection point along the contour.
-			unsigned int vert_seg_dir_valid_mask = 
-                (going_up ? 
-                    (iSegAbove != -1 && seg.intersections[iAbove].type == SegmentIntersection::INNER_LOW) :
-                    (iSegBelow != -1 && seg.intersections[iBelow].type == SegmentIntersection::INNER_HIGH)) ?
-                (DIR_FORWARD | DIR_BACKWARD) :
-                0;
-            {
-                // Invalidate iPrev resp. iNext, if the perimeter crosses the current vertical line earlier than iPrev resp. iNext.
-                // The perimeter contour orientation.
-                const bool forward = intrsctn->is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour);
-                const Polygon &poly = poly_with_offset.contour(intrsctn->iContour);
-                {
-                    int d_horiz = (iPrev     == -1) ? std::numeric_limits<int>::max() :
-                        distance_of_segmens(poly, segs[i_vline-1].intersections[iPrev].iSegment, intrsctn->iSegment, forward);
-                    int d_down  = (iSegBelow == -1) ? std::numeric_limits<int>::max() :
-                        distance_of_segmens(poly, iSegBelow, intrsctn->iSegment, forward);
-                    int d_up    = (iSegAbove == -1) ? std::numeric_limits<int>::max() :
-                        distance_of_segmens(poly, iSegAbove, intrsctn->iSegment, forward);
-                    if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up))
-                        // The vertical crossing comes eralier than the prev crossing.
-                        // Disable the perimeter going back.
-                        intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST;
-                    if (going_up ? (d_up > std::min(d_horiz, d_down)) : (d_down > std::min(d_horiz, d_up)))
-                        // The horizontal crossing comes earlier than the vertical crossing.
-                        vert_seg_dir_valid_mask &= ~(forward ? DIR_BACKWARD : DIR_FORWARD);
-                }
-                {
-                    int d_horiz = (iNext     == -1) ? std::numeric_limits<int>::max() :
-                        distance_of_segmens(poly, intrsctn->iSegment, segs[i_vline+1].intersections[iNext].iSegment, forward);
-                    int d_down  = (iSegBelow == -1) ? std::numeric_limits<int>::max() :
-                        distance_of_segmens(poly, intrsctn->iSegment, iSegBelow, forward);
-                    int d_up    = (iSegAbove == -1) ? std::numeric_limits<int>::max() :
-                        distance_of_segmens(poly, intrsctn->iSegment, iSegAbove, forward);
-                    if (intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up))
-                        // The vertical crossing comes eralier than the prev crossing.
-                        // Disable the perimeter going forward.
-                        intrsctn_type_next = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST;
-                    if (going_up ? (d_up > std::min(d_horiz, d_down)) : (d_down > std::min(d_horiz, d_up)))
-                        // The horizontal crossing comes earlier than the vertical crossing.
-                        vert_seg_dir_valid_mask &= ~(forward ? DIR_FORWARD : DIR_BACKWARD);
-                }
-            }
-
-            // 4) Try to connect to a previous or next vertical line, making a zig-zag pattern.
-            if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) {
-                coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
-                    measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iPrev);
-                coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
-                    measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext);
-                // Take the shorter path.
-                //FIXME this may not be always the best strategy to take the shortest connection line now.
-                bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ?
-                    (distNext < distPrev) : 
-                    intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK;
-                assert(intrsctn->is_inner());
-                bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length);
-                if (skip) {
-                    // Just skip the connecting contour and start a new path.
-                    goto dont_connect;
-                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
-                    polylines_out.push_back(Polyline()); 
-                    polyline_current = &polylines_out.back(); 
-                    const SegmentedIntersectionLine &il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)];
-                    polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos()));
-                } else {
-                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
-                    emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next);
-                }
-                // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
-                if (iPrev != -1)
-                    segs[i_vline-1].intersections[iPrev].consumed_perimeter_right = true;
-                if (iNext != -1)
-                    intrsctn->consumed_perimeter_right = true;
-                //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed.
-                // Advance to the neighbor line.
-                if (take_next) {
-                    ++ i_vline;
-                    i_intersection = iNext;
-                } else {
-                    -- i_vline;
-                    i_intersection = iPrev;
-                }
-                continue;
-            } 
-
-            // 5) Try to connect to a previous or next point on the same vertical line.
-            if (vert_seg_dir_valid_mask) {
-                bool valid = true;
-                // Verify, that there is no intersection with the inner contour up to the end of the contour segment.
-				// Verify, that the successive segment has not been consumed yet.
-				if (going_up) {
-					if (seg.intersections[iAbove].consumed_vertical_up) {
-						valid = false;
-					} else {
-						for (int i = (int)i_intersection + 1; i < iAbove && valid; ++i)
-							if (seg.intersections[i].is_inner()) 
-								valid = false;
-					}
-                } else {
-					if (seg.intersections[iBelow-1].consumed_vertical_up) {
-						valid = false;
-					} else {
-						for (int i = iBelow + 1; i < (int)i_intersection && valid; ++i)
-							if (seg.intersections[i].is_inner()) 
-								valid = false;
-					}
-                }
-                if (valid) {
-                    const Polygon &poly = poly_with_offset.contour(intrsctn->iContour);
-                    int iNext    = going_up ? iAbove : iBelow;
-                    int iSegNext = going_up ? iSegAbove : iSegBelow;
-                    bool dir_forward = (vert_seg_dir_valid_mask == (DIR_FORWARD | DIR_BACKWARD)) ?
-                        // Take the shorter length between the current and the next intersection point.
-                        (distance_of_segmens(poly, intrsctn->iSegment, iSegNext, true) <
-                         distance_of_segmens(poly, intrsctn->iSegment, iSegNext, false)) :
-                        (vert_seg_dir_valid_mask == DIR_FORWARD);
-                    // Skip this perimeter line?
-                    bool skip = params.dont_connect;
-                    if (! skip && link_max_length > 0) {
-                        coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
-                            poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, dir_forward);
-                        skip = link_length > link_max_length;
-                    }
-                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
-                    if (skip) {
-                        // Just skip the connecting contour and start a new path.
-                        polylines_out.push_back(Polyline()); 
-                        polyline_current = &polylines_out.back();
-                        polyline_current->points.push_back(Point(seg.pos, seg.intersections[iNext].pos()));
-                    } else {
-                        // Consume the connecting contour and the next segment.
-                        emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, *polyline_current, dir_forward);
-                    }
-                    // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
-                    // If there are any outer intersection points skipped (bypassed) by the contour,
-                    // mark them as processed.
-                    if (going_up) {
-                        for (int i = (int)i_intersection; i < iAbove; ++ i)
-                            seg.intersections[i].consumed_vertical_up = true;
-                    } else {
-                        for (int i = iBelow; i < (int)i_intersection; ++ i)
-                            seg.intersections[i].consumed_vertical_up = true;
-                    }
-//                    seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true;
-                    intrsctn->consumed_perimeter_right = true;
-                    i_intersection = iNext;
-                    if (going_up)
-                        ++ intrsctn;
-                    else
-                        -- intrsctn;
-                    intrsctn->consumed_perimeter_right = true;
-                    continue;
-                }
-            }
-        dont_connect: 
-            // No way to continue the current polyline. Take the rest of the line up to the outer contour.
-            // This will finish the polyline, starting another polyline at a new point.
-            if (going_up)
-                ++ intrsctn;
-            else
-                -- intrsctn;
-        }
-
-        // Finish the current vertical line,
-        // reset the current vertical line to pick a new starting point in the next round.
-        assert(intrsctn->is_outer());
-        assert(intrsctn->is_high() == going_up);
-        pointLast = Point(seg.pos, intrsctn->pos());
-        polyline_current->points.push_back(pointLast);
-        // Handle duplicate points and zero length segments.
-        polyline_current->remove_duplicate_points();
-        assert(! polyline_current->has_duplicate_points());
-        // Handle nearly zero length edges.
-        if (polyline_current->points.size() <= 1 ||
-        	(polyline_current->points.size() == 2 &&
-        		std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON &&
-				std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON))
-            polylines_out.pop_back();
-        intrsctn = NULL;
-        i_intersection = -1;
-        polyline_current = NULL;
-    }
+    bool monotonous_infill = params.density > 0.99;
+    if (monotonous_infill) {
+		std::vector<MonotonousRegion> regions = generate_montonous_regions(segs);
+		connect_monotonous_regions(regions, segs);
+		std::mt19937_64 rng;
+		std::vector<MonotonousRegionLink> path = chain_monotonous_regions(regions, segs, rng);
+		polylines_from_paths(path, poly_with_offset, segs, polylines_out);
+	} else
+		traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out);
 
 #ifdef SLIC3R_DEBUG
     {
diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h
index 1cf946f8b..f6fbba994 100644
--- a/src/libslic3r/libslic3r.h
+++ b/src/libslic3r/libslic3r.h
@@ -25,6 +25,7 @@
 // Saves around 32% RAM after slicing step, 6.7% after G-code export (tested on PrusaSlicer 2.2.0 final).
 typedef int32_t coord_t;
 #else
+//FIXME At least FillRectilinear2 requires coord_t to be 32bit.
 typedef int64_t coord_t;
 #endif
 

From 07411e795c7df493996db649102ce6c9be95798c Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 22 Apr 2020 15:46:23 +0200
Subject: [PATCH 31/55] Search: Implemented SearchDialog. Search window on
 Plater: set flag SelectAll for search line

---
 src/slic3r/GUI/ImGuiWrapper.cpp      |   2 +-
 src/slic3r/GUI/KBShortcutsDialog.cpp |   1 +
 src/slic3r/GUI/MainFrame.cpp         |   5 +
 src/slic3r/GUI/Plater.cpp            |  21 ++
 src/slic3r/GUI/Plater.hpp            |   1 +
 src/slic3r/GUI/Search.cpp            | 322 ++++++++++++++-------------
 src/slic3r/GUI/Search.hpp            |  75 ++++---
 src/slic3r/GUI/Tab.cpp               |   7 +-
 src/slic3r/GUI/Tab.hpp               |   2 +-
 9 files changed, 241 insertions(+), 195 deletions(-)

diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 435dfbe60..f9437ed61 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -640,7 +640,7 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
         // The press on Esc key invokes editing of InputText (removes last changes)
         // So we should save previous value...
         std::string str = search_str;
-        ImGui::InputTextEx("", NULL, search_str, 20, search_size, 0, NULL, NULL);
+        ImGui::InputTextEx("", NULL, search_str, 20, search_size, ImGuiInputTextFlags_AutoSelectAll, NULL, NULL);
         edited = ImGui::IsItemEdited();
         if (edited)
             view_params.hovered_id = -1;
diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp
index 79a6cad20..9f31d23d8 100644
--- a/src/slic3r/GUI/KBShortcutsDialog.cpp
+++ b/src/slic3r/GUI/KBShortcutsDialog.cpp
@@ -134,6 +134,7 @@ void KBShortcutsDialog::fill_shortcuts()
         { ctrl + "C", L("Copy to clipboard") },
         { ctrl + "V", L("Paste from clipboard") },
         { "F5", L("Reload plater from disk") },
+        { ctrl + "F", L("Search") },
         // Window
         { ctrl + "1", L("Select Plater Tab") },
         { ctrl + "2", L("Select Print Settings Tab") },
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index c4a6f09b7..5ea137f01 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -732,6 +732,11 @@ void MainFrame::init_menubar()
         append_menu_item(editMenu, wxID_ANY, _(L("Re&load from disk")) + sep + "F5",
             _(L("Reload the plater from disk")), [this](wxCommandEvent&) { m_plater->reload_all_from_disk(); },
             "", nullptr, [this]() {return !m_plater->model().objects.empty(); }, this);
+
+        editMenu->AppendSeparator();
+        append_menu_item(editMenu, wxID_ANY, _(L("Searc&h")) + "\t" + GUI::shortkey_ctrl_prefix() + "F",
+            _(L("Find option")), [this](wxCommandEvent&) { m_plater->search(m_tabpanel->GetCurrentPage() == m_plater); },
+            "search", nullptr, [this]() {return true; }, this);
     }
 
     // Window menu
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index f79ebec27..5263dd64d 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -5713,6 +5713,27 @@ void Plater::paste_from_clipboard()
     p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
 }
 
+void Plater::search(bool plater_is_active)
+{
+    if (plater_is_active) {
+        wxKeyEvent evt;
+#ifdef __APPLE__
+        evt.m_keyCode = 'f';
+#else /* __APPLE__ */
+        evt.m_keyCode = WXK_CONTROL_F;
+#endif /* __APPLE__ */
+        evt.SetControlDown(true);
+        canvas3D()->on_char(evt);
+    }
+    else
+    {
+        wxPoint pos = this->ClientToScreen(wxPoint(0, 0));
+        pos.x += em_unit(this) * 40;
+        pos.y += em_unit(this) * 4;
+        p->sidebar->get_searcher().search_dialog->Popup(pos);
+    }
+}
+
 void Plater::msw_rescale()
 {
     p->preview->msw_rescale();
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 33a279ed6..52903525a 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -284,6 +284,7 @@ public:
 
     void copy_selection_to_clipboard();
     void paste_from_clipboard();
+    void search(bool plater_is_active);
 
     bool can_delete() const;
     bool can_delete_all() const;
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index eb62b8a3f..9ef8aa0d0 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -281,6 +281,17 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
     return true;
 }
 
+OptionsSearcher::OptionsSearcher()
+{
+    search_dialog = new SearchDialog(this);
+}
+
+OptionsSearcher::~OptionsSearcher()
+{
+    if (search_dialog)
+        search_dialog->Destroy();
+}
+
 void OptionsSearcher::init(std::vector<InputInfo> input_values)
 {
     options.clear();
@@ -499,200 +510,195 @@ void SearchCtrl::OnLeftUpInTextCtrl(wxEvent &event)
 
 
 //------------------------------------------
-//          PopupSearchList
+//          SearchDialog
 //------------------------------------------
 
-PopupSearchList::PopupSearchList(wxWindow* parent) :
-    wxPopupTransientWindow(parent, /*wxSTAY_ON_TOP*/wxWANTS_CHARS | wxBORDER_NONE)
+SearchDialog::SearchDialog(OptionsSearcher* searcher)
+    : GUI::DPIDialog(NULL, wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
+    searcher(searcher)
 {
-    panel = new wxPanel(this, wxID_ANY);
+    SetFont(GUI::wxGetApp().normal_font());
+    wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+    SetBackgroundColour(bgr_clr);
 
-    text = new wxTextCtrl(panel, 1);
-    list = new wxListBox(panel, 2, wxDefaultPosition, wxSize(GUI::wxGetApp().em_unit() * 40, -1));
-    check = new wxCheckBox(panel, 3, "Group");
+    default_string = _L("Type here to search");
+    int border = 10;
+
+    search_line = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize);
+
+    // wxWANTS_CHARS style is neede for process Enter key press
+    search_list = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(em_unit() * 40, em_unit() * 30), 0, NULL, wxWANTS_CHARS);
+
+    wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+    check_type      = new wxCheckBox(this, wxID_ANY, _L("Type"));
+    check_category  = new wxCheckBox(this, wxID_ANY, _L("Category"));
+    check_group     = new wxCheckBox(this, wxID_ANY, _L("Group"));
+
+    wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL);
+
+    check_sizer->Add(check_type,     0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border);
+    check_sizer->Add(check_category, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border);
+    check_sizer->Add(check_group,    0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border);
+    check_sizer->AddStretchSpacer(border);
+    check_sizer->Add(cancel_btn,     0, wxALIGN_CENTER_VERTICAL);
 
     wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
 
-    text->Bind(wxEVT_MOUSE_CAPTURE_CHANGED, [](wxEvent& e) {
-        int i = 0; });
+    topSizer->Add(search_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
+    topSizer->Add(search_list, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
+    topSizer->Add(check_sizer, 0, wxEXPAND | wxALL, border);
 
-//    text->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e) {
-    text->Bind(wxEVT_LEFT_UP, [this](wxEvent& e) {
-        text->SetValue("mrrrrrty");
-    });
+    search_line->Bind(wxEVT_TEXT,    &SearchDialog::OnInputText, this);
+    search_line->Bind(wxEVT_LEFT_UP, &SearchDialog::OnLeftUpInTextCtrl, this);
+    // process wxEVT_KEY_DOWN to navigate inside search_list, if ArrowUp/Down was pressed
+    search_line->Bind(wxEVT_KEY_DOWN,&SearchDialog::OnKeyDown, this);
 
-    text->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt)
-    {
-        wxPoint pt = wxGetMousePosition() - text->GetScreenPosition();
-        long pos;
-        text->HitTest(pt, &pos);
+    search_list->Bind(wxEVT_MOTION,  &SearchDialog::OnMouseMove, this);
+    search_list->Bind(wxEVT_LEFT_UP, &SearchDialog::OnMouseClick, this);
+    search_list->Bind(wxEVT_KEY_DOWN,&SearchDialog::OnKeyDown, this);
 
-        if (pos == wxTE_HT_UNKNOWN)
-            return;
+    check_type    ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
+    check_category->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
+    check_group   ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
 
-        list->SetSelection(wxNOT_FOUND);
-        text->SetSelection(0, pos);
-    });
+    this->Bind(wxEVT_LISTBOX, &SearchDialog::OnSelect, this);
 
-    text->Bind(wxEVT_TEXT, [this](wxCommandEvent& e)
-    {
-        text->SetSelection(0, 3);
-    });
-
-    this->Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& event) {
-        int key = event.GetKeyCode();
-
-        // change selected item in the list
-        if (key == WXK_UP || key == WXK_DOWN)
-        {
-            int selection = list->GetSelection();
-
-            if (key == WXK_UP && selection > 0)
-                selection--;
-            if (key == WXK_DOWN && selection < int(list->GetCount() - 1))
-                selection++;
-
-            list->Select(selection);
-        }
-        else
-            event.Skip(); // !Needed to have EVT_CHAR generated as well
-    });
-
-    this->Bind(wxEVT_CHAR, [this](wxKeyEvent& e) {
-        int key = e.GetKeyCode();
-        wxChar symbol = e.GetUnicodeKey();
-        search_str += symbol;
-
-        text->SetValue(search_str);
-    });
-
-
-    list->Append("One");
-    list->Append("Two");
-    list->Append("Three");
-
-    list->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt)
-        {
-            int selection = list->GetSelection();
-        });
-
-    list->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& evt)
-    {
-        int selection = list->GetSelection();
-        list->SetSelection(wxNOT_FOUND);
-
-        wxCommandEvent event(wxEVT_LISTBOX, list->GetId());
-        event.SetInt(selection);
-        event.SetEventObject(this);
-        ProcessEvent(event);
-
-        Dismiss();
-    });
-
-    list->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt)
-    {
-        wxPoint pt = wxGetMousePosition() - list->GetScreenPosition();
-        int selection = list->HitTest(pt);
-        list->Select(selection);
-    });
-
-    list->Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& event) {
-        int key = event.GetKeyCode();
-
-        // change selected item in the list
-        if (key == WXK_UP || key == WXK_DOWN)
-        {
-            int selection = list->GetSelection();
-
-            if (key == WXK_UP && selection > 0)
-                selection--;
-            if (key == WXK_DOWN && selection < int(list->GetCount() - 1))
-                selection++;
-
-            list->Select(selection);
-        }
-        // send wxEVT_LISTBOX event if "Enter" was pushed
-        else if (key == WXK_NUMPAD_ENTER || key == WXK_RETURN)
-        {
-            int selection = list->GetSelection();
-
-            wxCommandEvent event(wxEVT_LISTBOX, list->GetId());
-            event.SetInt(selection);
-            event.SetEventObject(this);
-            ProcessEvent(event);
-
-            Dismiss();
-        }
-        else
-            event.Skip(); // !Needed to have EVT_CHAR generated as well
-    });
-
-    topSizer->Add(text, 0, wxEXPAND | wxALL, 2);
-    topSizer->Add(list, 0, wxEXPAND | wxALL, 2);
-    topSizer->Add(check, 0, wxEXPAND | wxALL, 2);
-
-    panel->SetSizer(topSizer);
-
-    topSizer->Fit(panel);
-    SetClientSize(panel->GetSize());
+    SetSizer(topSizer);
+    topSizer->SetSizeHints(this);
 }
 
-void PopupSearchList::Popup(wxWindow* WXUNUSED(focus))
+void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/)
 {
-    wxPopupTransientWindow::Popup();
+    const std::string& line = searcher->search_string();
+    search_line->SetValue(line.empty() ? default_string : from_u8(line));
+    search_line->SetFocus();
+    search_line->SelectAll();
+
+    update_list();
+
+    const OptionViewParameters& params = searcher->view_params;
+    check_type->SetValue(params.type);
+    check_category->SetValue(params.category);
+    check_group->SetValue(params.group);
+
+    this->SetPosition(position);
+    this->ShowModal();
 }
 
-void PopupSearchList::OnDismiss()
+void SearchDialog::ProcessSelection(int selection)
 {
-    wxPopupTransientWindow::OnDismiss();
+    if (selection < 0)
+        return;
+
+    GUI::wxGetApp().sidebar().jump_to_option(selection);
+    this->EndModal(wxID_CLOSE);
 }
 
-bool PopupSearchList::ProcessLeftDown(wxMouseEvent& event)
+void SearchDialog::OnInputText(wxCommandEvent&)
 {
-    return wxPopupTransientWindow::ProcessLeftDown(event);
-}
-bool PopupSearchList::Show(bool show)
-{
-    return wxPopupTransientWindow::Show(show);
+    search_line->SetInsertionPointEnd();
+
+    wxString input_string = search_line->GetValue();
+    if (input_string == default_string)
+        input_string.Clear();
+
+    GUI::wxGetApp().sidebar().get_search_line() = into_u8(input_string);
+
+    GUI::wxGetApp().sidebar().search_and_apply_tab_search_lines();
+
+    update_list();
 }
 
-void PopupSearchList::OnSize(wxSizeEvent& event)
+void SearchDialog::OnLeftUpInTextCtrl(wxEvent& event)
 {
+    if (search_line->GetValue() == default_string)
+        search_line->SetValue("");
+
     event.Skip();
 }
 
-void PopupSearchList::OnSetFocus(wxFocusEvent& event)
+void SearchDialog::OnMouseMove(wxMouseEvent& event)
 {
-    event.Skip();
+    wxPoint pt = wxGetMousePosition() - search_list->GetScreenPosition();
+    int selection = search_list->HitTest(pt);
+    search_list->Select(selection);
 }
 
-void PopupSearchList::OnKillFocus(wxFocusEvent& event)
+void SearchDialog::OnMouseClick(wxMouseEvent&)
 {
-    event.Skip();
+    int selection = search_list->GetSelection();
+    search_list->SetSelection(wxNOT_FOUND);
+
+    wxCommandEvent event(wxEVT_LISTBOX, search_list->GetId());
+    event.SetInt(selection);
+    event.SetEventObject(search_list);
+    ProcessEvent(event);
 }
 
-
-//------------------------------------------
-//          SearchCtrl
-//------------------------------------------
-
-SearchButton::SearchButton(wxWindow* parent) :
-    ScalableButton(parent, wxID_ANY, "search")
+void SearchDialog::OnSelect(wxCommandEvent& event)
 {
-    popup_win = new PopupSearchList(parent);
-    this->Bind(wxEVT_BUTTON, &SearchButton::PopupSearch, this);
+    int selection = event.GetSelection();
+    ProcessSelection(selection);
 }
-    
-void SearchButton::PopupSearch(wxCommandEvent& e)
+
+void SearchDialog::update_list()
 {
-//    popup_win->update_list(wxGetApp().sidebar().get_search_list().filters);
-    wxPoint pos = this->ClientToScreen(wxPoint(0, 0));
-    wxSize sz = wxSize(GUI::wxGetApp().em_unit()*40, -1);
-    pos.x -= sz.GetWidth();
-    pos.y += this->GetSize().y;
-    popup_win->Position(pos, sz);
-    popup_win->Popup();
-    e.Skip();
+    search_list->Clear();
+
+    const std::vector<FoundOption>& filters = searcher->found_options();
+    for (const FoundOption& item : filters)
+        search_list->Append(item.label);
+}
+
+void SearchDialog::OnKeyDown(wxKeyEvent& event)
+{
+    int key = event.GetKeyCode();
+
+    // change selected item in the list
+    if (key == WXK_UP || key == WXK_DOWN)
+    {
+        int selection = search_list->GetSelection();
+
+        if (key == WXK_UP && selection > 0)
+            selection--;
+        if (key == WXK_DOWN && selection < int(search_list->GetCount() - 1))
+            selection++;
+
+        search_list->Select(selection);
+        // This function could be called from search_line,
+        // So, for the next correct navigation, set focus on the search_list
+        search_list->SetFocus();
+    }
+    // process "Enter" pressed
+    else if (key == WXK_NUMPAD_ENTER || key == WXK_RETURN)
+        ProcessSelection(search_list->GetSelection());
+    else
+        event.Skip(); // !Needed to have EVT_CHAR generated as well
+}
+
+void SearchDialog::OnCheck(wxCommandEvent& event)
+{
+    OptionViewParameters& params = searcher->view_params;
+    params.type     = check_type->GetValue();
+    params.category = check_category->GetValue();
+    params.group    = check_group->GetValue();
+
+    searcher->search(searcher->search_string(), true);
+    update_list();
+}
+
+void SearchDialog::on_dpi_changed(const wxRect& suggested_rect)
+{
+    const int& em = em_unit();
+
+    msw_buttons_rescale(this, em, { wxID_CANCEL });
+
+    const wxSize& size = wxSize(40 * em, 30 * em);
+    SetMinSize(size);
+
+    Fit();
+    Refresh();
 }
 
 
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 7933527af..e7c1f6f58 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -10,9 +10,10 @@
 
 #include <wx/combo.h>
 
-#include <wx/popupwin.h>
 #include <wx/checkbox.h>
+#include <wx/dialog.h>
 
+#include "GUI_Utils.hpp"
 #include "Preset.hpp"
 #include "wxExtensions.hpp"
 
@@ -21,6 +22,8 @@ namespace Slic3r {
 
 namespace Search{
 
+class SearchDialog;
+
 struct InputInfo
 {
     DynamicPrintConfig* config  {nullptr};
@@ -112,6 +115,11 @@ class OptionsSearcher
 public:
     OptionViewParameters                    view_params;
 
+    SearchDialog*                           search_dialog { nullptr };
+
+    OptionsSearcher();
+    ~OptionsSearcher();
+
     void init(std::vector<InputInfo> input_values);
     void apply(DynamicPrintConfig *config,
                Preset::Type        type,
@@ -127,6 +135,7 @@ public:
 
     const std::vector<FoundOption>& found_options() { return found; }
     const GroupAndCategory&         get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; }
+    const std::string& search_string() { return search_line; }
 };
 
 
@@ -187,44 +196,46 @@ public:
 };
 
 
+//------------------------------------------
+//          SearchDialog
+//------------------------------------------
 
-class PopupSearchList : public wxPopupTransientWindow
+class SearchDialog : public GUI::DPIDialog
 {
-    wxString search_str; 
+    wxString search_str;
+    wxString default_string;
+
+    wxTextCtrl*     search_line    { nullptr };
+    wxListBox*      search_list    { nullptr };
+    wxCheckBox*     check_type     { nullptr };
+    wxCheckBox*     check_category { nullptr };
+    wxCheckBox*     check_group    { nullptr };
+
+    OptionsSearcher* searcher;
+
+    void update_list();
+
+    void OnInputText(wxCommandEvent& event);
+    void OnLeftUpInTextCtrl(wxEvent& event);
+    
+    void OnMouseMove(wxMouseEvent& event); 
+    void OnMouseClick(wxMouseEvent& event);
+    void OnSelect(wxCommandEvent& event);
+    void OnKeyDown(wxKeyEvent& event);
+
+    void OnCheck(wxCommandEvent& event);
+
 public:
-    PopupSearchList(wxWindow* parent);
-    ~PopupSearchList() {}
+    SearchDialog(OptionsSearcher* searcher);
+    ~SearchDialog() {}
 
-    // wxPopupTransientWindow virtual methods are all overridden to log them
-    void Popup(wxWindow* focus = NULL) wxOVERRIDE;
-    void OnDismiss() wxOVERRIDE;
-    bool ProcessLeftDown(wxMouseEvent& event) wxOVERRIDE;
-    bool Show(bool show = true) wxOVERRIDE;
+    void Popup(wxPoint position = wxDefaultPosition);
+    void ProcessSelection(int selection);
 
-private:
-    wxWindow* panel;
-
-    wxTextCtrl* text {nullptr};
-    wxListBox*  list{ nullptr };
-    wxCheckBox* check {nullptr};
-
-    void OnSize(wxSizeEvent& event);
-    void OnSetFocus(wxFocusEvent& event);
-    void OnKillFocus(wxFocusEvent& event);
+protected:
+    void on_dpi_changed(const wxRect& suggested_rect) override;
 };
 
-class SearchButton : public ScalableButton
-{
-    PopupSearchList* popup_win{ nullptr };
-
-    void PopupSearch(wxCommandEvent& event);
-public:
-    SearchButton(wxWindow* parent);
-    ~SearchButton() {}
-};
-
-
-
 
 } // Search namespace
 }
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 1dc25ed2f..9be89005a 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -160,8 +160,6 @@ void Tab::create_preset_tab()
 
     // search combox
     m_search = new Search::SearchCtrl(panel);
-    // search combox
-    m_search_btn = new Search::SearchButton(panel);
 
     auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
@@ -184,10 +182,12 @@ void Tab::create_preset_tab()
     m_btn_delete_preset->Disable();
 
     add_scaled_button(panel, &m_question_btn, "question");
-
     m_question_btn->SetToolTip(_(L("Hover the cursor over buttons to find more information \n"
                                    "or click this button.")));
 
+    add_scaled_button(panel, &m_search_btn, "search");
+    m_question_btn->SetToolTip(_L("Find option"));
+
     // Determine the theme color of OS (dark or light)
     auto luma = wxGetApp().get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
     // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
@@ -218,6 +218,7 @@ void Tab::create_preset_tab()
             }
         }
     }));
+    m_search_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { wxGetApp().plater()->search(false); });
 
     // Colors for ui "decoration"
     m_sys_label_clr			= wxGetApp().get_label_clr_sys();
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 3b9717ccb..90eb2b6bb 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -123,7 +123,7 @@ protected:
 	const wxString		m_title;
 	PresetBitmapComboBox*	m_presets_choice;
 	Search::SearchCtrl*	m_search;
-	Search::SearchButton*	m_search_btn;
+	ScalableButton*		m_search_btn;
 	ScalableButton*		m_btn_save_preset;
 	ScalableButton*		m_btn_delete_preset;
 	ScalableButton*		m_btn_hide_incompatible_presets;

From b447b45a3e2b31c71741543777956615519e09e5 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Wed, 22 Apr 2020 20:27:42 +0200
Subject: [PATCH 32/55] Search: Fixed "Search" menu item under OSX  + deleted
 search combobox

---
 src/slic3r/GUI/MainFrame.cpp |  2 +-
 src/slic3r/GUI/Plater.cpp    |  2 +-
 src/slic3r/GUI/Tab.cpp       | 11 +++--------
 3 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 5ea137f01..12a4451ec 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -734,7 +734,7 @@ void MainFrame::init_menubar()
             "", nullptr, [this]() {return !m_plater->model().objects.empty(); }, this);
 
         editMenu->AppendSeparator();
-        append_menu_item(editMenu, wxID_ANY, _(L("Searc&h")) + "\t" + GUI::shortkey_ctrl_prefix() + "F",
+        append_menu_item(editMenu, wxID_ANY, _(L("Searc&h")) + "\tCtrl+F",
             _(L("Find option")), [this](wxCommandEvent&) { m_plater->search(m_tabpanel->GetCurrentPage() == m_plater); },
             "search", nullptr, [this]() {return true; }, this);
     }
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 5263dd64d..db3fdd281 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1094,7 +1094,7 @@ void Sidebar::msw_rescale()
 void Sidebar::search_and_apply_tab_search_lines(bool force/* = false*/)
 {
     if (p->searcher.search(p->search_line, force))
-        apply_search_line_on_tabs();
+        ;//apply_search_line_on_tabs();
 }
 
 void Sidebar::jump_to_option(size_t selected)
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 9be89005a..c5410933b 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -159,7 +159,7 @@ void Tab::create_preset_tab()
     m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1));
 
     // search combox
-    m_search = new Search::SearchCtrl(panel);
+//    m_search = new Search::SearchCtrl(panel);
 
     auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
@@ -240,16 +240,11 @@ void Tab::create_preset_tab()
     m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->AddSpacer(int(8 * scale_factor));
     m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
+    m_hsizer->AddSpacer(int(32 * scale_factor));
     m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
+    m_hsizer->AddSpacer(int(48 * scale_factor));
     m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->AddSpacer(int(/*32*/16 * scale_factor));
-    m_hsizer->Add(m_search, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->AddSpacer(int(16 * scale_factor));
-//    m_hsizer->AddSpacer(int(32 * scale_factor));
-//    m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
     // m_hsizer->AddStretchSpacer(32);
     // StretchSpacer has a strange behavior under OSX, so
     // There is used just additional sizer for m_mode_sizer with right alignment

From fcb85dcdc306233241d28252a10556feed207017 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 23 Apr 2020 12:41:38 +0200
Subject: [PATCH 33/55] First implementation of the separate plater from the
 settings tabs + Added collapse_toolbar

---
 src/slic3r/GUI/GLCanvas3D.cpp  | 161 ++++++++++++++++++++++++++++-----
 src/slic3r/GUI/GLCanvas3D.hpp  |   7 +-
 src/slic3r/GUI/GUI_App.cpp     |   2 +
 src/slic3r/GUI/GUI_Preview.cpp |   1 +
 src/slic3r/GUI/MainFrame.cpp   |  52 ++++++++---
 src/slic3r/GUI/MainFrame.hpp   |   4 +-
 src/slic3r/GUI/Plater.cpp      |   9 ++
 src/slic3r/GUI/Tab.cpp         |  15 ++-
 src/slic3r/GUI/Tab.hpp         |   1 +
 9 files changed, 211 insertions(+), 41 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index e7075298c..6c3436ea1 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1557,6 +1557,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
 #endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
     , m_main_toolbar(GLToolbar::Normal, "Top")
     , m_undoredo_toolbar(GLToolbar::Normal, "Top")
+    , m_collapse_toolbar(GLToolbar::Normal, "TopRight")
     , m_gizmos(*this)
     , m_use_clipping_planes(false)
     , m_sidebar_field("")
@@ -1960,6 +1961,11 @@ void GLCanvas3D::enable_undoredo_toolbar(bool enable)
     m_undoredo_toolbar.set_enabled(enable);
 }
 
+void GLCanvas3D::enable_collapse_toolbar(bool enable)
+{
+    m_collapse_toolbar.set_enabled(enable);
+}
+
 void GLCanvas3D::enable_dynamic_background(bool enable)
 {
     m_dynamic_background_enabled = enable;
@@ -2185,6 +2191,9 @@ void GLCanvas3D::render()
 	        tooltip = m_undoredo_toolbar.get_tooltip();
 
 	    if (tooltip.empty())
+	        tooltip = m_collapse_toolbar.get_tooltip();
+
+	    if (tooltip.empty())
 #if ENABLE_NON_STATIC_CANVAS_MANAGER
             tooltip = wxGetApp().plater()->get_view_toolbar().get_tooltip();
 #else
@@ -2223,6 +2232,9 @@ void GLCanvas3D::render()
     if (tooltip.empty())
         tooltip = m_undoredo_toolbar.get_tooltip();
 
+    if (tooltip.empty())
+        tooltip = m_collapse_toolbar.get_tooltip();
+
     if (tooltip.empty())
         tooltip = m_view_toolbar.get_tooltip();
 
@@ -2949,6 +2961,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
 
     m_dirty |= m_main_toolbar.update_items_state();
     m_dirty |= m_undoredo_toolbar.update_items_state();
+    m_dirty |= m_collapse_toolbar.update_items_state();
 #if ENABLE_NON_STATIC_CANVAS_MANAGER
     m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state();
     bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
@@ -3580,6 +3593,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
         return;
     }
 
+    if (m_collapse_toolbar.on_mouse(evt, *this))
+    {
+        if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
+            mouse_up_cleanup();
+        m_mouse.set_start_position_3D_as_invalid();
+        return;
+    }
+
 #if ENABLE_NON_STATIC_CANVAS_MANAGER
     if (wxGetApp().plater()->get_view_toolbar().on_mouse(evt, *this))
 #else
@@ -4478,7 +4499,7 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
 #else
     const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
 #endif
-    imgui->set_next_window_pos(x, m_undoredo_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
+    imgui->set_next_window_pos(x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
     std::string title = L("Search");
     imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
 
@@ -4894,6 +4915,9 @@ bool GLCanvas3D::_init_toolbars()
     if (!_init_view_toolbar())
         return false;
 
+    if (!_init_collapse_toolbar())
+        return false;
+
     return true;
 }
 
@@ -5183,26 +5207,6 @@ bool GLCanvas3D::_init_undoredo_toolbar()
     if (!m_undoredo_toolbar.add_separator())
         return false;
 
-    item.name = "collapse_sidebar";
-    item.icon_filename = "collapse.svg";
-    item.tooltip = wxGetApp().plater()->is_sidebar_collapsed() ? 
-                   _utf8(L("Expand right panel")) : _utf8(L("Collapse right panel"));
-    item.sprite_id = 2;
-    item.left.action_callback = [this, item]() {
-        std::string new_tooltip = wxGetApp().plater()->is_sidebar_collapsed() ? 
-                                  _utf8(L("Collapse right panel")) : _utf8(L("Expand right panel"));
-
-        int id = m_undoredo_toolbar.get_item_id("collapse_sidebar");
-        m_undoredo_toolbar.set_tooltip(id, new_tooltip);
-        set_tooltip("");
-
-        post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR));
-    };
-
-    item.enabling_callback = []()->bool { return true; };
-    if (!m_undoredo_toolbar.add_item(item))
-        return false;
-
     return true;
 }
 
@@ -5211,6 +5215,86 @@ bool GLCanvas3D::_init_view_toolbar()
     return wxGetApp().plater()->init_view_toolbar();
 }
 
+bool GLCanvas3D::_init_collapse_toolbar()
+{
+    if (!m_collapse_toolbar.is_enabled())
+        return true;
+
+    BackgroundTexture::Metadata background_data;
+    background_data.filename = "toolbar_background.png";
+    background_data.left = 16;
+    background_data.top = 16;
+    background_data.right = 16;
+    background_data.bottom = 16;
+
+    if (!m_collapse_toolbar.init(background_data))
+    {
+        // unable to init the toolbar texture, disable it
+        m_collapse_toolbar.set_enabled(false);
+        return true;
+    }
+
+    m_collapse_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
+    m_collapse_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Right);
+    m_collapse_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top);
+    m_collapse_toolbar.set_border(5.0f);
+    m_collapse_toolbar.set_separator_size(5);
+    m_collapse_toolbar.set_gap_size(2);
+
+    GLToolbarItem::Data item;
+
+    item.name = "collapse_sidebar";
+    item.icon_filename = "collapse.svg";
+    item.tooltip =  wxGetApp().plater()->is_sidebar_collapsed() ? _utf8(L("Expand right panel")) : _utf8(L("Collapse right panel"));
+    item.sprite_id = 0;
+    item.left.action_callback = [this, item]() {
+        std::string new_tooltip = wxGetApp().plater()->is_sidebar_collapsed() ?
+            _utf8(L("Collapse right panel")) : _utf8(L("Expand right panel"));
+
+        int id = m_collapse_toolbar.get_item_id("collapse_sidebar");
+        m_collapse_toolbar.set_tooltip(id, new_tooltip);
+        set_tooltip("");
+
+        post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR));
+    };
+
+    if (!m_collapse_toolbar.add_item(item))
+        return false;
+
+    if (!m_collapse_toolbar.add_separator())
+        return false;
+
+    item.name = "print";
+    item.icon_filename = "cog.svg";
+    item.tooltip = _utf8(L("Switch to Print Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "2]";
+    item.sprite_id = 1;
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(0); };
+
+    if (!m_collapse_toolbar.add_item(item))
+        return false;
+
+    item.name = "filament";
+    item.icon_filename = "spool.svg";
+    item.tooltip = _utf8(L("Switch to Filament Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
+    item.sprite_id = 2;
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(1); };
+
+    if (!m_collapse_toolbar.add_item(item))
+        return false;
+
+    item.name = "printer";
+    item.icon_filename = "printer.svg";
+    item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]";
+    item.sprite_id = 3;
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(2); };
+
+    if (!m_collapse_toolbar.add_item(item))
+        return false;
+
+    return true;
+
+}
+
 bool GLCanvas3D::_set_current()
 {
     return m_context != nullptr && m_canvas->SetCurrent(*m_context);
@@ -5588,14 +5672,17 @@ void GLCanvas3D::_render_overlays() const
     const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true);
     m_main_toolbar.set_scale(scale);
     m_undoredo_toolbar.set_scale(scale);
+    m_collapse_toolbar.set_scale(scale);
 #else
     const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(true));
     m_main_toolbar.set_icons_size(size);
     m_undoredo_toolbar.set_icons_size(size);
+    m_collapse_toolbar.set_icons_size(size);
 #endif // ENABLE_RETINA_GL
 
     _render_main_toolbar();
     _render_undoredo_toolbar();
+    _render_collapse_toolbar();
     _render_view_toolbar();
 
     if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f))
@@ -5726,6 +5813,27 @@ void GLCanvas3D::_render_undoredo_toolbar() const
     m_undoredo_toolbar.render(*this);
 }
 
+void GLCanvas3D::_render_collapse_toolbar() const
+{
+    if (!m_collapse_toolbar.is_enabled())
+        return;
+
+    Size cnv_size = get_canvas_size();
+#if ENABLE_NON_STATIC_CANVAS_MANAGER
+    float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
+#else
+    float inv_zoom = (float)m_camera.get_inv_zoom();
+#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
+
+    float band = m_layers_editing.is_enabled() ? (wxGetApp().imgui()->get_style_scaling() * LayersEditing::THICKNESS_BAR_WIDTH) : 0.0;
+
+    float top  = 0.5f * (float)cnv_size.get_height() * inv_zoom;
+    float left = (0.5f * (float)cnv_size.get_width() - (float)m_collapse_toolbar.get_width() - band) * inv_zoom;
+
+    m_collapse_toolbar.set_position(top, left);
+    m_collapse_toolbar.render(*this);
+}
+
 void GLCanvas3D::_render_view_toolbar() const
 {
 #if ENABLE_NON_STATIC_CANVAS_MANAGER
@@ -7275,6 +7383,17 @@ bool GLCanvas3D::_activate_search_toolbar_item()
     return false;
 }
 
+bool GLCanvas3D::_deactivate_collapse_toolbar_items()
+{
+    if (m_collapse_toolbar.is_item_pressed("print"))
+    {
+        m_collapse_toolbar.force_left_action(m_collapse_toolbar.get_item_id("print"), *this);
+        return true;
+    }
+
+    return false;
+}
+
 const Print* GLCanvas3D::fff_print() const
 {
     return (m_process == nullptr) ? nullptr : m_process->fff_print();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 60f62636d..73ec88a13 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -160,8 +160,8 @@ private:
             Num_States
         };
 
-    private:
         static const float THICKNESS_BAR_WIDTH;
+    private:
 
         bool                        m_enabled;
         Shader                      m_shader;
@@ -456,6 +456,7 @@ private:
     mutable GLGizmosManager m_gizmos;
     mutable GLToolbar m_main_toolbar;
     mutable GLToolbar m_undoredo_toolbar;
+    mutable GLToolbar m_collapse_toolbar;
     ClippingPlane m_clipping_planes[2];
     mutable ClippingPlane m_camera_clipping_plane;
     bool m_use_clipping_planes;
@@ -602,6 +603,7 @@ public:
     void enable_selection(bool enable);
     void enable_main_toolbar(bool enable);
     void enable_undoredo_toolbar(bool enable);
+    void enable_collapse_toolbar(bool enable);
     void enable_dynamic_background(bool enable);
     void enable_labels(bool enable) { m_labels.enable(enable); }
 #if ENABLE_SLOPE_RENDERING
@@ -742,6 +744,7 @@ private:
     bool _init_main_toolbar();
     bool _init_undoredo_toolbar();
     bool _init_view_toolbar();
+    bool _init_collapse_toolbar();
 
     bool _set_current();
     void _resize(unsigned int w, unsigned int h);
@@ -770,6 +773,7 @@ private:
     void _render_gizmos_overlay() const;
     void _render_main_toolbar() const;
     void _render_undoredo_toolbar() const;
+    void _render_collapse_toolbar() const;
     void _render_view_toolbar() const;
 #if ENABLE_SHOW_CAMERA_TARGET
     void _render_camera_target() const;
@@ -839,6 +843,7 @@ private:
     bool _deactivate_undo_redo_toolbar_items();
     bool _deactivate_search_toolbar_item();
     bool _activate_search_toolbar_item();
+    bool _deactivate_collapse_toolbar_items();
 
     static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
 
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 794226520..a7c1b1dd5 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -424,6 +424,8 @@ bool GUI_App::on_init_inner()
 
     update_mode(); // update view mode after fix of the object_list size
 
+    mainframe->switch_to(true); // hide settings tabs after mode updating
+
     m_initialized = true;
     return true;
 }
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 29ece9b31..47a0d3074 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -97,6 +97,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_
     m_canvas->enable_selection(true);
     m_canvas->enable_main_toolbar(true);
     m_canvas->enable_undoredo_toolbar(true);
+    m_canvas->enable_collapse_toolbar(true);
     m_canvas->enable_labels(true);
 #if ENABLE_SLOPE_RENDERING
     m_canvas->enable_slope(true);
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 12a4451ec..48c5fe99b 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -89,6 +89,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
 
     // initialize layout
     auto sizer = new wxBoxSizer(wxVERTICAL);
+    if (m_plater)
+        sizer->Add(m_plater, 1, wxEXPAND);
     if (m_tabpanel)
         sizer->Add(m_tabpanel, 1, wxEXPAND);
     sizer->SetSizeHints(this);
@@ -306,9 +308,11 @@ void MainFrame::init_tabpanel()
         }
     });
 
-    m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
+//!    m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
+    m_plater = new Plater(this, this);
+
     wxGetApp().plater_ = m_plater;
-    m_tabpanel->AddPage(m_plater, _(L("Plater")));
+//    m_tabpanel->AddPage(m_plater, _(L("Plater")));
 
     wxGetApp().obj_list()->create_popup_menus();
 
@@ -334,6 +338,13 @@ void MainFrame::init_tabpanel()
     }
 }
 
+void MainFrame::switch_to(bool plater)
+{
+    this->m_plater->Show(plater);
+    this->m_tabpanel->Show(!plater);
+    this->Layout();
+}
+
 void MainFrame::create_preset_tabs()
 {
     wxGetApp().update_label_colours_from_appconfig();
@@ -735,33 +746,34 @@ void MainFrame::init_menubar()
 
         editMenu->AppendSeparator();
         append_menu_item(editMenu, wxID_ANY, _(L("Searc&h")) + "\tCtrl+F",
-            _(L("Find option")), [this](wxCommandEvent&) { m_plater->search(m_tabpanel->GetCurrentPage() == m_plater); },
+            _(L("Find option")), [this](wxCommandEvent&) { m_plater->search(/*m_tabpanel->GetCurrentPage() == */m_plater->IsShown()); },
             "search", nullptr, [this]() {return true; }, this);
     }
 
     // Window menu
     auto windowMenu = new wxMenu();
     {
-        size_t tab_offset = 0;
+//!        size_t tab_offset = 0;
         if (m_plater) {
             append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")),
-                [this](wxCommandEvent&) { select_tab(0); }, "plater", nullptr,
+                [this/*, tab_offset*/](wxCommandEvent&) { select_tab((size_t)(-1)); }, "plater", nullptr,
                 [this]() {return true; }, this);
-            tab_offset += 1;
-        }
-        if (tab_offset > 0) {
+//!            tab_offset += 1;
+//!        }
+//!        if (tab_offset > 0) {
             windowMenu->AppendSeparator();
         }
         append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")),
-            [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog", nullptr,
+            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + */0); }, "cog", nullptr,
             [this]() {return true; }, this);
         wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")),
-            [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool", nullptr,
+            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + */1); }, "spool", nullptr,
             [this]() {return true; }, this);
         m_changeable_menu_items.push_back(item_material_tab);
-        append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")),
-            [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer", nullptr,
+        wxMenuItem* item_printer_tab = append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")),
+            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + */2); }, "printer", nullptr,
             [this]() {return true; }, this);
+        m_changeable_menu_items.push_back(item_printer_tab);
         if (m_plater) {
             windowMenu->AppendSeparator();
             append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")),
@@ -905,7 +917,9 @@ void MainFrame::update_menubar()
     m_changeable_menu_items[miSend]         ->SetItemLabel((is_fff ? _(L("S&end G-code"))           : _(L("S&end to print"))) + dots    + "\tCtrl+Shift+G");
 
     m_changeable_menu_items[miMaterialTab]  ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab")))   + "\tCtrl+3");
-    m_changeable_menu_items[miMaterialTab]  ->SetBitmap(create_scaled_bitmap(is_fff ? "spool": "resin"));
+    m_changeable_menu_items[miMaterialTab]  ->SetBitmap(create_scaled_bitmap(is_fff ? "spool"   : "resin"));
+
+    m_changeable_menu_items[miPrinterTab]   ->SetBitmap(create_scaled_bitmap(is_fff ? "printer" : "sla_printer"));
 }
 
 // To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
@@ -1221,9 +1235,17 @@ void MainFrame::load_config(const DynamicPrintConfig& config)
 #endif
 }
 
-void MainFrame::select_tab(size_t tab) const
+void MainFrame::select_tab(size_t tab)
 {
-    m_tabpanel->SetSelection(tab);
+    if (tab == (size_t)(-1)) {
+        if (m_plater && !m_plater->IsShown())
+            this->switch_to(true);
+    }
+    else {
+        if (m_plater && m_plater->IsShown())
+            switch_to(false);
+        m_tabpanel->SetSelection(tab);
+    }
 }
 
 // Set a camera direction, zoom to all objects.
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 6038e6d2f..8fc0ed1f2 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -86,6 +86,7 @@ class MainFrame : public DPIFrame
         miExport = 0,   // Export G-code        Export
         miSend,         // Send G-code          Send to print
         miMaterialTab,  // Filament Settings    Material Settings
+        miPrinterTab,   // Different bitmap for Printer Settings
     };
 
     // vector of a MenuBar items changeable in respect to printer technology 
@@ -108,6 +109,7 @@ public:
     void        update_title();
 
     void        init_tabpanel();
+    void        switch_to(bool plater);
     void        create_preset_tabs();
     void        add_created_tab(Tab* panel);
     void        init_menubar();
@@ -128,7 +130,7 @@ public:
     void        export_configbundle();
     void        load_configbundle(wxString file = wxEmptyString);
     void        load_config(const DynamicPrintConfig& config);
-    void        select_tab(size_t tab) const;
+    void        select_tab(size_t tab);
     void        select_view(const std::string& direction);
     // Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
     void        on_config_changed(DynamicPrintConfig* cfg) const ;
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index db3fdd281..8c7a59fa1 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -362,6 +362,9 @@ PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)),
 
         wxGetApp().tab_panel()->ChangeSelection(page_id);
 
+        // Switch to Settings NotePad
+        wxGetApp().mainframe->switch_to(false);
+
         /* In a case of a multi-material printing, for editing another Filament Preset
          * it's needed to select this preset for the "Filament settings" Tab
          */
@@ -773,6 +776,8 @@ Sidebar::Sidebar(Plater *parent)
     p->scrolled = new wxScrolledWindow(this);
     p->scrolled->SetScrollbars(0, 100, 1, 2);
 
+    SetFont(wxGetApp().normal_font());
+    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
 
     // Sizer in the scrolled area
     auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL);
@@ -1101,6 +1106,10 @@ void Sidebar::jump_to_option(size_t selected)
 {
     const Search::Option& opt = p->searcher.get_option(selected);
     wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key, opt.category);
+
+    // Switch to the Settings NotePad, if plater is shown
+    if (p->plater->IsShown())
+        wxGetApp().mainframe->switch_to(false);
 }
 
 ObjectManipulation* Sidebar::obj_manipul()
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index c5410933b..dfb2c9d4a 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -31,6 +31,7 @@
 #include "GUI_App.hpp"
 #include "GUI_ObjectList.hpp"
 #include "ConfigWizard.hpp"
+#include "format.hpp"
 
 namespace Slic3r {
 namespace GUI {
@@ -186,7 +187,10 @@ void Tab::create_preset_tab()
                                    "or click this button.")));
 
     add_scaled_button(panel, &m_search_btn, "search");
-    m_question_btn->SetToolTip(_L("Find option"));
+    m_search_btn->SetToolTip(format_wxstr(_L("Click to start a search or use %1% shortcut"), "Ctrl+F"));
+
+    add_scaled_button(panel, &m_to_plater_btn, "plater");
+    m_to_plater_btn->SetToolTip(_L("Switch to the Plater"));
 
     // Determine the theme color of OS (dark or light)
     auto luma = wxGetApp().get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
@@ -219,6 +223,7 @@ void Tab::create_preset_tab()
         }
     }));
     m_search_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { wxGetApp().plater()->search(false); });
+    m_to_plater_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { wxGetApp().mainframe->switch_to(true); });
 
     // Colors for ui "decoration"
     m_sys_label_clr			= wxGetApp().get_label_clr_sys();
@@ -243,13 +248,17 @@ void Tab::create_preset_tab()
     m_hsizer->AddSpacer(int(32 * scale_factor));
     m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
     m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
-    m_hsizer->AddSpacer(int(48 * scale_factor));
+    m_hsizer->AddSpacer(int(32 * scale_factor));
     m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL);
     // m_hsizer->AddStretchSpacer(32);
     // StretchSpacer has a strange behavior under OSX, so
     // There is used just additional sizer for m_mode_sizer with right alignment
+    wxBoxSizer* top_right_sizer = new wxBoxSizer(wxHORIZONTAL);
+    top_right_sizer->Add(m_to_plater_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, wxOSX ? 15 : 10);
+    top_right_sizer->Add(m_mode_sizer, 0, wxALIGN_CENTER_VERTICAL);
     auto mode_sizer = new wxBoxSizer(wxVERTICAL);
-    mode_sizer->Add(m_mode_sizer, 1, wxALIGN_RIGHT);
+//    mode_sizer->Add(m_mode_sizer, 1, wxALIGN_RIGHT);
+    mode_sizer->Add(top_right_sizer, 1, wxALIGN_RIGHT);
     m_hsizer->Add(mode_sizer, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, wxOSX ? 15 : 10);
 
     //Horizontal sizer to hold the tree and the selected page.
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 90eb2b6bb..9d91abdcf 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -127,6 +127,7 @@ protected:
 	ScalableButton*		m_btn_save_preset;
 	ScalableButton*		m_btn_delete_preset;
 	ScalableButton*		m_btn_hide_incompatible_presets;
+	ScalableButton*		m_to_plater_btn;
 	wxBoxSizer*			m_hsizer;
 	wxBoxSizer*			m_left_sizer;
 	wxTreeCtrl*			m_treectrl;

From 1c1a7ed71279577e9b65f8a1ac5a7573e51802c3 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 23 Apr 2020 21:00:00 +0200
Subject: [PATCH 34/55] Collapse_toolbar: fixed draw items + Extended toolbar

---
 src/slic3r/GUI/GLCanvas3D.cpp | 28 ++++++++++++++++++++++++----
 src/slic3r/GUI/GUI_App.cpp    |  4 ++--
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 6c3436ea1..02f92168c 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1557,7 +1557,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
 #endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
     , m_main_toolbar(GLToolbar::Normal, "Top")
     , m_undoredo_toolbar(GLToolbar::Normal, "Top")
-    , m_collapse_toolbar(GLToolbar::Normal, "TopRight")
+    , m_collapse_toolbar(GLToolbar::Normal, "Top")
     , m_gizmos(*this)
     , m_use_clipping_planes(false)
     , m_sidebar_field("")
@@ -5261,14 +5261,12 @@ bool GLCanvas3D::_init_collapse_toolbar()
     if (!m_collapse_toolbar.add_item(item))
         return false;
 
-    if (!m_collapse_toolbar.add_separator())
-        return false;
-
     item.name = "print";
     item.icon_filename = "cog.svg";
     item.tooltip = _utf8(L("Switch to Print Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "2]";
     item.sprite_id = 1;
     item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(0); };
+    item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed(); };
 
     if (!m_collapse_toolbar.add_item(item))
         return false;
@@ -5278,6 +5276,8 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.tooltip = _utf8(L("Switch to Filament Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
     item.sprite_id = 2;
     item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(1); };
+    item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed() &&
+                                                  wxGetApp().plater()->printer_technology() == ptFFF; };
 
     if (!m_collapse_toolbar.add_item(item))
         return false;
@@ -5288,6 +5288,26 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.sprite_id = 3;
     item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(2); };
 
+    if (!m_collapse_toolbar.add_item(item))
+        return false;
+
+    item.name = "resin";
+    item.icon_filename = "resin.svg";
+    item.tooltip = _utf8(L("Switch to SLA Material Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
+    item.sprite_id = 4;
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(1); };
+    item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed() &&
+                                                  (m_process->current_printer_technology() == ptSLA); };
+
+    if (!m_collapse_toolbar.add_item(item))
+        return false;
+
+    item.name = "sla_printer";
+    item.icon_filename = "sla_printer.svg";
+    item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]";
+    item.sprite_id = 5;
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(2); };
+
     if (!m_collapse_toolbar.add_item(item))
         return false;
 
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index a7c1b1dd5..81654bb64 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -373,6 +373,8 @@ bool GUI_App::on_init_inner()
     if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
         wxImage::AddHandler(new wxPNGHandler());
     mainframe = new MainFrame();
+    mainframe->switch_to(true); // hide settings tabs after first Layout
+
     sidebar().obj_list()->init_objects(); // propagate model objects to object list
 //     update_mode(); // !!! do that later
     SetTopWindow(mainframe);
@@ -424,8 +426,6 @@ bool GUI_App::on_init_inner()
 
     update_mode(); // update view mode after fix of the object_list size
 
-    mainframe->switch_to(true); // hide settings tabs after mode updating
-
     m_initialized = true;
     return true;
 }

From 3ba4a2cf3d1bb8f53e3ce5fb761fed4cd02597be Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Thu, 23 Apr 2020 21:32:12 +0200
Subject: [PATCH 35/55] Fixed an update of the search list after the change of
 the print technology

---
 src/slic3r/GUI/Plater.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 8c7a59fa1..149bc95ae 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3687,9 +3687,6 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
      * and for SLA presets they should be deleted
      */
         wxGetApp().obj_list()->update_object_list_by_printer_technology();
-
-        // print technology could be changed, so we should to update a search list
-        sidebar->update_searcher();
     }
 }
 
@@ -5423,8 +5420,11 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
         }
         
         p->config->set_key_value(opt_key, config.option(opt_key)->clone());
-        if (opt_key == "printer_technology")
+        if (opt_key == "printer_technology") {
             this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
+            // print technology is changed, so we should to update a search list
+            p->sidebar->update_searcher();
+        }
         else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) {
             bed_shape_changed = true;
             update_scheduled = true;

From e390ebc95c09db6947d7c009108aa4fbf66f90ad Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 24 Apr 2020 09:41:48 +0200
Subject: [PATCH 36/55] WIP: Monotonous infill

---
 src/libslic3r/Fill/FillRectilinear2.cpp | 834 +++++++++++++-----------
 src/libslic3r/GCode.cpp                 |   9 +-
 2 files changed, 473 insertions(+), 370 deletions(-)

diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp
index c2c19046e..3d5710d29 100644
--- a/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -179,8 +179,8 @@ struct SegmentIntersection
     // Kept grouped with other booleans for smaller memory footprint.
     LinkType 		prev_on_contour_type { LinkType::Horizontal };
     LinkType 		next_on_contour_type { LinkType::Horizontal };
-    LinkQuality 	prev_on_contour_quality { true };
-    LinkQuality 	next_on_contour_quality { true };
+    LinkQuality 	prev_on_contour_quality { LinkQuality::Valid };
+    LinkQuality 	next_on_contour_quality { LinkQuality::Valid };
     // Was this segment along the y axis consumed?
     // Up means up along the vertical segment.
     bool 	 		consumed_vertical_up { false };
@@ -237,36 +237,34 @@ struct SegmentIntersection
     int 	left_vertical(Direction dir) 		const { return (dir == Direction::Up ? this->has_left_vertical_up() : this->has_left_vertical_down()) ? this->prev_on_contour : -1; }
     int 	left_vertical()   			 		const { return this->has_left_vertical() 	   ? this->prev_on_contour : -1; }
     int 	left_vertical_outside()				const { return this->is_low() ? this->left_vertical_down() : this->left_vertical_up(); }
-    int 	right_vertical_up()   		 		const { return this->has_right_vertical_up()   ? this->prev_on_contour : -1; }
-    int 	right_vertical_down()   	 		const { return this->has_right_vertical_down() ? this->prev_on_contour : -1; }
+    int 	right_vertical_up()   		 		const { return this->has_right_vertical_up()   ? this->next_on_contour : -1; }
+    int 	right_vertical_down()   	 		const { return this->has_right_vertical_down() ? this->next_on_contour : -1; }
     int 	right_vertical(Direction dir) 		const { return (dir == Direction::Up ? this->has_right_vertical_up() : this->has_right_vertical_down()) ? this->next_on_contour : -1; }
-    int 	right_vertical()   			 		const { return this->has_right_vertical() 	   ? this->prev_on_contour : -1; }
+    int 	right_vertical()   			 		const { return this->has_right_vertical() 	   ? this->next_on_contour : -1; }
     int 	right_vertical_outside()			const { return this->is_low() ? this->right_vertical_down() : this->right_vertical_up(); }
 
     int 	vertical_up(Side side)				const { return side == Side::Left ? this->left_vertical_up() : this->right_vertical_up(); }
     int 	vertical_down(Side side)			const { return side == Side::Left ? this->left_vertical_down() : this->right_vertical_down(); }
     int 	vertical_outside(Side side)			const { return side == Side::Left ? this->left_vertical_outside() : this->right_vertical_outside(); }
+    // Returns -1 if there is no link up.
     int 	vertical_up()						const { 
-    	assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up());
     	return this->has_left_vertical_up() ? this->left_vertical_up() : this->right_vertical_up();
     }
     LinkQuality vertical_up_quality()			const {
-    	assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up());
+    	assert(this->has_left_vertical_up() != this->has_right_vertical_up());
     	return this->has_left_vertical_up() ? this->prev_on_contour_quality : this->next_on_contour_quality;
     }
+    // Returns -1 if there is no link down.
     int 	vertical_down()						const {
-    	assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down());
+//    	assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down());
     	return this->has_left_vertical_down() ? this->left_vertical_down() : this->right_vertical_down();
     }
     LinkQuality vertical_down_quality()			const {
-    	assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down());
+    	assert(this->has_left_vertical_down() != this->has_right_vertical_down());
     	return this->has_left_vertical_down() ? this->prev_on_contour_quality : this->next_on_contour_quality;
     }
     int 	vertical_outside()					const { return this->is_low() ? this->vertical_down() : this->vertical_up(); }
 
-//    int  	next_up()    const { return this->prev_on_contour_vertical ? -1 : this->prev_on_contour; }
-//    int  	next_right() const { return this->next_on_contour_vertical ? -1 : this->next_on_contour; }
-
     // Compare two y intersection points given by rational numbers.
     // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32.
     // This function calculates pos_p * other.pos_q < other.pos_p * pos_q as a 48bit number.
@@ -489,32 +487,32 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
     size_t                                         iIntersection,
     SegmentIntersection::Side                      side)
 {
-    const SegmentedIntersectionLine &il_this      = segs[iVerticalLine];
-    const SegmentIntersection       &itsct_this   = il_this.intersections[iIntersection];
-	if (itsct_this.has_vertical(side))
+    const SegmentedIntersectionLine &vline_this = segs[iVerticalLine];
+    const SegmentIntersection       &it_this    = vline_this.intersections[iIntersection];
+	if (it_this.has_vertical(side))
 	    // Not the first intersection along the contor. This intersection point
 	    // has been preceded by an intersection point along the vertical line.
 		return INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST;
-    int iIntersectionOther = itsct_this.horizontal(side);
+    int iIntersectionOther = it_this.horizontal(side);
     if (iIntersectionOther == -1)
         return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
     assert(side == SegmentIntersection::Side::Right ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
-    const SegmentedIntersectionLine &il_other     = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine+1) : (iVerticalLine-1)];
-    const SegmentIntersection       &itsct_other  = il_other.intersections[iIntersectionOther];
-    assert(itsct_other.is_inner());
+    const SegmentedIntersectionLine &vline_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine + 1) : (iVerticalLine - 1)];
+    const SegmentIntersection       &it_other    = vline_other.intersections[iIntersectionOther];
+    assert(it_other.is_inner());
     assert(iIntersectionOther > 0);
-    assert(iIntersectionOther + 1 < il_other.intersections.size());
+    assert(iIntersectionOther + 1 < vline_other.intersections.size());
     // Is iIntersectionOther at the boundary of a vertical segment?
-    const SegmentIntersection       &itsct_other2 = il_other.intersections[itsct_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1];
-    if (itsct_other2.is_inner())
+    const SegmentIntersection       &it_other2   = vline_other.intersections[it_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1];
+    if (it_other2.is_inner())
         // Cannot follow a perimeter segment into the middle of another vertical segment.
         // Only perimeter segments connecting to the end of a vertical segment are followed.
         return INTERSECTION_TYPE_OTHER_VLINE_INNER;
-    assert(itsct_other.is_low() == itsct_other2.is_low());
-    if (side == SegmentIntersection::Side::Right ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right)
+    assert(it_other.is_low() == it_other2.is_low());
+    if (side == SegmentIntersection::Side::Right ? it_this.consumed_perimeter_right : it_other.consumed_perimeter_right)
         // This perimeter segment was already consumed.
         return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED;
-    if (itsct_other.is_low() ? itsct_other.consumed_vertical_up : il_other.intersections[iIntersectionOther-1].consumed_vertical_up)
+    if (it_other.is_low() ? it_other.consumed_vertical_up : vline_other.intersections[iIntersectionOther - 1].consumed_vertical_up)
         // This vertical segment was already consumed.
         return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED;
     return INTERSECTION_TYPE_OTHER_VLINE_OK;
@@ -555,23 +553,23 @@ static inline coordf_t measure_perimeter_prev_next_segment_length(
         return coordf_t(-1);
     }
 
-    const SegmentedIntersectionLine &il     = segs[iVerticalLine];
-    const SegmentIntersection       &itsct  = il.intersections[iIntersection];
-    const SegmentedIntersectionLine &il2    = segs[iVerticalLineOther];
-    const SegmentIntersection       &itsct2 = il2.intersections[iIntersection2];
-    assert(itsct.iContour == itsct2.iContour);
-    const Polygon                   &poly   = poly_with_offset.contour(itsct.iContour);
-//    const bool                       ccw    = poly_with_offset.is_contour_ccw(il.iContour);
-    assert(itsct.type == itsct2.type);
-    assert(itsct.iContour == itsct2.iContour);
-    assert(itsct.is_inner());
-    const bool                       forward = itsct.is_low() == dir_is_next;
+    const SegmentedIntersectionLine &vline  = segs[iVerticalLine];
+    const SegmentIntersection       &it     = vline.intersections[iIntersection];
+    const SegmentedIntersectionLine &vline2 = segs[iVerticalLineOther];
+    const SegmentIntersection       &it2    = vline2.intersections[iIntersection2];
+    assert(it.iContour == it2.iContour);
+    const Polygon                   &poly   = poly_with_offset.contour(it.iContour);
+//    const bool                       ccw    = poly_with_offset.is_contour_ccw(vline.iContour);
+    assert(it.type == it2.type);
+    assert(it.iContour == it2.iContour);
+    assert(it.is_inner());
+    const bool                       forward = it.is_low() == dir_is_next;
 
-    Point p1(il.pos, itsct.pos());
-    Point p2(il2.pos, itsct2.pos());
+    Point p1(vline.pos,  it.pos());
+    Point p2(vline2.pos, it2.pos());
     return forward ?
-        segment_length(poly, itsct .iSegment, p1, itsct2.iSegment, p2) :
-        segment_length(poly, itsct2.iSegment, p2, itsct .iSegment, p1);
+        segment_length(poly, it .iSegment, p1, it2.iSegment, p2) :
+        segment_length(poly, it2.iSegment, p2, it .iSegment, p1);
 }
 
 static inline coordf_t measure_perimeter_prev_segment_length(
@@ -736,10 +734,10 @@ static inline float measure_outer_contour_slab(
                 distance_of_segmens(poly, iSegBelow, itsct.iSegment, true);
             int d_up    = (iAbove == -1) ? std::numeric_limits<int>::max() :
                 distance_of_segmens(poly, iSegAbove, itsct.iSegment, true);
-            if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up))
+            if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up))
                 // The vertical crossing comes eralier than the prev crossing.
                 // Disable the perimeter going back.
-                intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST;
+                intrsection_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST;
             if (d_up > std::min(d_horiz, d_down))
                 // The horizontal crossing comes earlier than the vertical crossing.
                 vert_seg_dir_valid_mask &= ~DIR_BACKWARD;
@@ -918,7 +916,7 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
     }
 
     // Verify the segments. If something is wrong, give up.
-#define ASSERT_THROW(CONDITION) do { assert(CONDITION); throw InfillFailedException(); } while (0)
+#define ASSERT_THROW(CONDITION) do { assert(CONDITION); if (! (CONDITION)) throw InfillFailedException(); } while (0)
     for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) {
         SegmentedIntersectionLine &sil = segs[i_seg];
         // The intersection points have to be even.
@@ -952,18 +950,19 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
 	    const SegmentedIntersectionLine *il_prev = i_vline > 0 ? &segs[i_vline - 1] : nullptr;
 	    const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr;
 
-        for (size_t i_intersection = 0; i_intersection + 1 < il.intersections.size(); ++ i_intersection) {
+        for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) {
 		    SegmentIntersection &itsct = il.intersections[i_intersection];
 	        const Polygon 		&poly  = poly_with_offset.contour(itsct.iContour);
 
 	        // 1) Find possible connection points on the previous / next vertical line.
-		    // Find an intersection point on iVerticalLineOther, intersecting iInnerContour
-		    // at the same orientation as iIntersection, and being closest to iIntersection
+		    // Find an intersection point on il_prev, intersecting i_intersection
+		    // at the same orientation as i_intersection, and being closest to i_intersection
 		    // in the number of contour segments, when following the direction of the contour.
+            //FIXME this has O(n) time complexity. Likely an O(log(n)) scheme is possible.
 		    int iprev = -1;
 		    if (il_prev) {
 			    int dmin = std::numeric_limits<int>::max();
-			    for (size_t i = 0; i < il_prev->intersections.size(); ++ i) {
+			    for (int i = 0; i < il_prev->intersections.size(); ++ i) {
 			        const SegmentIntersection &itsct2 = il_prev->intersections[i];
 			        if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
 			            // The intersection points lie on the same contour and have the same orientation.
@@ -976,10 +975,11 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
 			        }
 			    }
 			}
+            // The same for il_next.
 		    int inext = -1;
 		    if (il_next) {
 			    int dmin = std::numeric_limits<int>::max();
-			    for (size_t i = 0; i < il_next->intersections.size(); ++ i) {
+			    for (int i = 0; i < il_next->intersections.size(); ++ i) {
 			        const SegmentIntersection &itsct2 = il_next->intersections[i];
 			        if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
 			            // The intersection points lie on the same contour and have the same orientation.
@@ -996,14 +996,14 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
 	        // 2) Find possible connection points on the same vertical line.
 	        int iabove = -1;
             // Does the perimeter intersect the current vertical line above intrsctn?
-            for (size_t i = i_intersection + 1; i + 1 < il.intersections.size(); ++ i)
+            for (int i = i_intersection + 1; i < il.intersections.size(); ++ i)
                 if (il.intersections[i].iContour == itsct.iContour) {
                     iabove = i;
                     break;
                 }
             // Does the perimeter intersect the current vertical line below intrsctn?
 	        int ibelow = -1;
-            for (size_t i = i_intersection - 1; i > 0; -- i)
+            for (int i = i_intersection - 1; i >= 0; -- i)
                 if (il.intersections[i].iContour == itsct.iContour) {
                     ibelow = i;
                     break;
@@ -1019,7 +1019,7 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
 	            int d_down  = (ibelow == -1) ? std::numeric_limits<int>::max() :
 	                distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward);
 	            int d_up    = (iabove == -1) ? std::numeric_limits<int>::max() :
-	                distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward);
+	                distance_of_segmens(poly, il.intersections[iabove].iSegment, itsct.iSegment, forward);
 	            if (d_horiz < std::min(d_down, d_up)) {
                     itsct.prev_on_contour 	    = iprev;
                     itsct.prev_on_contour_type  = SegmentIntersection::LinkType::Horizontal;
@@ -1061,7 +1061,7 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
 // Such intersection shall always exist.
 static const SegmentIntersection& end_of_vertical_run_raw(const SegmentIntersection &start)
 {
-	assert(start.type != SegmentIntersection::INNER_LOW);
+	assert(start.type == SegmentIntersection::INNER_LOW);
     // Step back to the beginning of the vertical segment to mark it as consumed.
     auto *it = &start;
     do {
@@ -1083,7 +1083,7 @@ static SegmentIntersection& end_of_vertical_run_raw(SegmentIntersection &start)
 // Such intersection shall always exist.
 static const SegmentIntersection& end_of_vertical_run(const SegmentedIntersectionLine &il, const SegmentIntersection &start)
 {
-	assert(start.type != SegmentIntersection::INNER_LOW);
+	assert(start.type == SegmentIntersection::INNER_LOW);
 	const SegmentIntersection *end = &end_of_vertical_run_raw(start);
 	assert(end->type == SegmentIntersection::INNER_HIGH);
 	for (;;) {
@@ -1263,17 +1263,17 @@ static void traverse_graph_generate_polylines(
 {
     // For each outer only chords, measure their maximum distance to the bow of the outer contour.
     // Mark an outer only chord as consumed, if the distance is low.
-    for (size_t i_vline = 0; i_vline < segs.size(); ++i_vline) {
-        SegmentedIntersectionLine& seg = segs[i_vline];
-        for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++i_intersection) {
-            if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW &&
-                seg.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) {
+    for (int i_vline = 0; i_vline < segs.size(); ++ i_vline) {
+        SegmentedIntersectionLine &vline = segs[i_vline];
+        for (int i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) {
+            if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW &&
+                vline.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) {
                 bool consumed = false;
                 //                if (params.full_infill()) {
                 //                        measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection);
                 //                } else
                 consumed = true;
-                seg.intersections[i_intersection].consumed_vertical_up = consumed;
+                vline.intersections[i_intersection].consumed_vertical_up = consumed;
             }
         }
     }
@@ -1283,34 +1283,34 @@ static void traverse_graph_generate_polylines(
     // Naively one would expect to achieve best results by chaining the paths by the shortest distance,
     // but that procedure does not create the longest continuous paths.
     // A simple "sweep left to right" procedure achieves better results.
-    size_t    i_vline = 0;
-    size_t    i_intersection = size_t(-1);
+    int    	  i_vline = 0;
+    int    	  i_intersection = -1;
     // Follow the line, connect the lines into a graph.
     // Until no new line could be added to the output path:
     Point     pointLast;
-    Polyline* polyline_current = NULL;
-    if (!polylines_out.empty())
+    Polyline* polyline_current = nullptr;
+    if (! polylines_out.empty())
         pointLast = polylines_out.back().points.back();
     for (;;) {
-        if (i_intersection == size_t(-1)) {
+        if (i_intersection == -1) {
             // The path has been interrupted. Find a next starting point, closest to the previous extruder position.
             coordf_t dist2min = std::numeric_limits<coordf_t>().max();
-            for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++i_vline2) {
-                const SegmentedIntersectionLine& seg = segs[i_vline2];
-                if (!seg.intersections.empty()) {
-                    assert(seg.intersections.size() > 1);
+            for (int i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) {
+                const SegmentedIntersectionLine &vline = segs[i_vline2];
+                if (! vline.intersections.empty()) {
+                    assert(vline.intersections.size() > 1);
                     // Even number of intersections with the loops.
-                    assert((seg.intersections.size() & 1) == 0);
-                    assert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW);
-                    for (size_t i = 0; i < seg.intersections.size(); ++i) {
-                        const SegmentIntersection& intrsctn = seg.intersections[i];
+                    assert((vline.intersections.size() & 1) == 0);
+                    assert(vline.intersections.front().type == SegmentIntersection::OUTER_LOW);
+                    for (int i = 0; i < vline.intersections.size(); ++ i) {
+                        const SegmentIntersection& intrsctn = vline.intersections[i];
                         if (intrsctn.is_outer()) {
                             assert(intrsctn.is_low() || i > 0);
                             bool consumed = intrsctn.is_low() ?
                                 intrsctn.consumed_vertical_up :
-                                seg.intersections[i - 1].consumed_vertical_up;
-                            if (!consumed) {
-                                coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
+                                vline.intersections[i - 1].consumed_vertical_up;
+                            if (! consumed) {
+                                coordf_t dist2 = sqr(coordf_t(pointLast(0) - vline.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
                                 if (dist2 < dist2min) {
                                     dist2min = dist2;
                                     i_vline = i_vline2;
@@ -1326,7 +1326,7 @@ static void traverse_graph_generate_polylines(
                     }
                 }
             }
-            if (i_intersection == size_t(-1))
+            if (i_intersection == -1)
                 // We are finished.
                 break;
         found:
@@ -1339,220 +1339,196 @@ static void traverse_graph_generate_polylines(
         }
 
         // From the initial point (i_vline, i_intersection), follow a path.
-        SegmentedIntersectionLine& seg = segs[i_vline];
-        SegmentIntersection* intrsctn = &seg.intersections[i_intersection];
-        bool going_up = intrsctn->is_low();
-        bool try_connect = false;
+        SegmentedIntersectionLine &vline 		= segs[i_vline];
+        SegmentIntersection 	  *it 			= &vline.intersections[i_intersection];
+        bool 					   going_up 	= it->is_low();
+        bool 					   try_connect 	= false;
         if (going_up) {
-            assert(!intrsctn->consumed_vertical_up);
-            assert(i_intersection + 1 < seg.intersections.size());
+            assert(! it->consumed_vertical_up);
+            assert(i_intersection + 1 < vline.intersections.size());
             // Step back to the beginning of the vertical segment to mark it as consumed.
-            if (intrsctn->is_inner()) {
+            if (it->is_inner()) {
                 assert(i_intersection > 0);
-                --intrsctn;
-                --i_intersection;
+                -- it;
+                -- i_intersection;
             }
             // Consume the complete vertical segment up to the outer contour.
             do {
-                intrsctn->consumed_vertical_up = true;
-                ++intrsctn;
-                ++i_intersection;
-                assert(i_intersection < seg.intersections.size());
-            } while (intrsctn->type != SegmentIntersection::OUTER_HIGH);
-            if ((intrsctn - 1)->is_inner()) {
+                it->consumed_vertical_up = true;
+                ++ it;
+                ++ i_intersection;
+                assert(i_intersection < vline.intersections.size());
+            } while (it->type != SegmentIntersection::OUTER_HIGH);
+            if ((it - 1)->is_inner()) {
                 // Step back.
-                --intrsctn;
-                --i_intersection;
-                assert(intrsctn->type == SegmentIntersection::INNER_HIGH);
+                -- it;
+                -- i_intersection;
+                assert(it->type == SegmentIntersection::INNER_HIGH);
                 try_connect = true;
             }
         } else {
             // Going down.
-            assert(intrsctn->is_high());
+            assert(it->is_high());
             assert(i_intersection > 0);
-            assert(!(intrsctn - 1)->consumed_vertical_up);
+            assert(!(it - 1)->consumed_vertical_up);
             // Consume the complete vertical segment up to the outer contour.
-            if (intrsctn->is_inner())
-                intrsctn->consumed_vertical_up = true;
+            if (it->is_inner())
+                it->consumed_vertical_up = true;
             do {
                 assert(i_intersection > 0);
-                --intrsctn;
-                --i_intersection;
-                intrsctn->consumed_vertical_up = true;
-            } while (intrsctn->type != SegmentIntersection::OUTER_LOW);
-            if ((intrsctn + 1)->is_inner()) {
+                -- it;
+                -- i_intersection;
+                it->consumed_vertical_up = true;
+            } while (it->type != SegmentIntersection::OUTER_LOW);
+            if ((it + 1)->is_inner()) {
                 // Step back.
-                ++intrsctn;
-                ++i_intersection;
-                assert(intrsctn->type == SegmentIntersection::INNER_LOW);
+                ++ it;
+                ++ i_intersection;
+                assert(it->type == SegmentIntersection::INNER_LOW);
                 try_connect = true;
             }
         }
         if (try_connect) {
             // Decide, whether to finish the segment, or whether to follow the perimeter.
-
             // 1) Find possible connection points on the previous / next vertical line.
-            IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection);
+            IntersectionTypeOtherVLine intrsection_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection);
             IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection);
             // Try to connect to a previous or next vertical line, making a zig-zag pattern.
-            if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) {
-            	int iPrev = intrsctn->left_horizontal();
-            	int iNext = intrsctn->right_horizontal();
-                coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
-                    measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, iPrev);
-                coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
-                    measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, iNext);
+            if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) {
+            	// A horizontal connection along the perimeter line exists.
+            	int i_prev = it->left_horizontal();
+            	int i_next = it->right_horizontal();
+                coordf_t dist_prev = (intrsection_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
+                    measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_prev);
+                coordf_t dist_next = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
+                    measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next);
                 // Take the shorter path.
                 //FIXME this may not be always the best strategy to take the shortest connection line now.
-                bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ?
-                    (distNext < distPrev) :
+                bool take_next = (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ?
+                    (dist_next < dist_prev) :
                     intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK;
-                assert(intrsctn->is_inner());
-                bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length);
+                assert(it->is_inner());
+                bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? dist_next : dist_prev) > link_max_length);
                 if (skip) {
+#if 1
                     // Just skip the connecting contour and start a new path.
                     goto dont_connect;
-                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
-                    polylines_out.push_back(Polyline());
+#else                    
+                    polyline_current->points.emplace_back(vline.pos, it->pos());
+                    polylines_out.emplace_back();
                     polyline_current = &polylines_out.back();
                     const SegmentedIntersectionLine& il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)];
-                    polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos()));
+                    polyline_current->points.emplace_back(il2.pos, il2.intersections[take_next ? i_next : i_prev].pos());
+#endif
                 } else {
-                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
-                    emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next);
+                    polyline_current->points.emplace_back(vline.pos, it->pos());
+                    emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next);
                 }
                 // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
-                if (iPrev != -1)
-                    segs[i_vline - 1].intersections[iPrev].consumed_perimeter_right = true;
-                if (iNext != -1)
-                    intrsctn->consumed_perimeter_right = true;
+                if (i_prev != -1)
+                    segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
+                if (i_next != -1)
+                    it->consumed_perimeter_right = true;
                 //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed.
                 // Advance to the neighbor line.
                 if (take_next) {
-                    ++i_vline;
-                    i_intersection = iNext;
+                    ++ i_vline;
+                    i_intersection = i_next;
                 }
                 else {
-                    --i_vline;
-                    i_intersection = iPrev;
+                    -- i_vline;
+                    i_intersection = i_prev;
                 }
                 continue;
             }
 
             // 5) Try to connect to a previous or next point on the same vertical line.
-            if (int inext = intrsctn->vertical_outside(); inext != -1) {
+            if (int inext = it->vertical_outside(); inext != -1) {
                 bool valid = true;
                 // Verify, that there is no intersection with the inner contour up to the end of the contour segment.
                 // Verify, that the successive segment has not been consumed yet.
                 if (going_up) {
-                    if (seg.intersections[inext].consumed_vertical_up)
+                    if (vline.intersections[inext].consumed_vertical_up)
                         valid = false;
                     else {
-                        for (int i = (int)i_intersection + 1; i < inext && valid; ++i)
-                            if (seg.intersections[i].is_inner())
+                        for (int i = i_intersection + 1; i < inext && valid; ++ i)
+                            if (vline.intersections[i].is_inner())
                                 valid = false;
                     }
                 } else {
-                    if (seg.intersections[inext - 1].consumed_vertical_up)
+                    if (vline.intersections[inext - 1].consumed_vertical_up)
                         valid = false;
                     else {
-                        for (int i = inext + 1; i < (int)i_intersection && valid; ++i)
-                            if (seg.intersections[i].is_inner())
+                        for (int i = inext + 1; i < i_intersection && valid; ++ i)
+                            if (vline.intersections[i].is_inner())
                                 valid = false;
                     }
                 }
                 if (valid) {
-                    const Polygon& poly = poly_with_offset.contour(intrsctn->iContour);
-                    assert(intrsctn->iContour == seg.intersections[inext].iContour);
-                    int iSegNext = seg.intersections[inext].iSegment;
+                    const Polygon &poly = poly_with_offset.contour(it->iContour);
+                    assert(it->iContour == vline.intersections[inext].iContour);
                     // Skip this perimeter line?
                     bool skip = params.dont_connect;
-                    bool dir_forward = intrsctn->has_right_vertical_outside();
+                    bool dir_forward = it->has_right_vertical_outside();
                     if (! skip && link_max_length > 0) {
                         coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
                             poly_with_offset, segs, i_vline, i_intersection, inext, dir_forward);
                         skip = link_length > link_max_length;
                     }
-                    polyline_current->points.push_back(Point(seg.pos, intrsctn->pos()));
+                    polyline_current->points.emplace_back(vline.pos, it->pos());
                     if (skip) {
                         // Just skip the connecting contour and start a new path.
-                        polylines_out.push_back(Polyline());
+                        polylines_out.emplace_back();
                         polyline_current = &polylines_out.back();
-                        polyline_current->points.push_back(Point(seg.pos, seg.intersections[inext].pos()));
+                        polyline_current->points.emplace_back(vline.pos, vline.intersections[inext].pos());
                     } else {
                         // Consume the connecting contour and the next segment.
-                        emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline_current, dir_forward);
+                        emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, inext, *polyline_current, dir_forward);
                     }
                     // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
                     // If there are any outer intersection points skipped (bypassed) by the contour,
                     // mark them as processed.
-                    if (going_up) {
-                        for (int i = (int)i_intersection; i < inext; ++i)
-                            seg.intersections[i].consumed_vertical_up = true;
-                    } else {
-                        for (int i = inext; i < (int)i_intersection; ++i)
-                            seg.intersections[i].consumed_vertical_up = true;
-                    }
-                    // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true;
-                    intrsctn->consumed_perimeter_right = true;
-                    i_intersection = inext;
                     if (going_up)
-                        ++intrsctn;
+                        for (int i = i_intersection; i < inext; ++ i)
+                            vline.intersections[i].consumed_vertical_up = true;
                     else
-                        --intrsctn;
-                    intrsctn->consumed_perimeter_right = true;
+                        for (int i = inext; i < i_intersection; ++ i)
+                            vline.intersections[i].consumed_vertical_up = true;
+                    // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true;
+                    it->consumed_perimeter_right = true;
+                    (going_up ? ++ it : -- it)->consumed_perimeter_right = true;
+                    i_intersection = inext;
                     continue;
                 }
             }
+
         dont_connect:
             // No way to continue the current polyline. Take the rest of the line up to the outer contour.
             // This will finish the polyline, starting another polyline at a new point.
-            if (going_up)
-                ++intrsctn;
-            else
-                --intrsctn;
+            going_up ? ++ it : -- it;
         }
 
         // Finish the current vertical line,
         // reset the current vertical line to pick a new starting point in the next round.
-        assert(intrsctn->is_outer());
-        assert(intrsctn->is_high() == going_up);
-        pointLast = Point(seg.pos, intrsctn->pos());
-        polyline_current->points.push_back(pointLast);
+        assert(it->is_outer());
+        assert(it->is_high() == going_up);
+        pointLast = Point(vline.pos, it->pos());
+        polyline_current->points.emplace_back(pointLast);
         // Handle duplicate points and zero length segments.
         polyline_current->remove_duplicate_points();
-        assert(!polyline_current->has_duplicate_points());
+        assert(! polyline_current->has_duplicate_points());
         // Handle nearly zero length edges.
         if (polyline_current->points.size() <= 1 ||
             (polyline_current->points.size() == 2 &&
                 std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON &&
                 std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON))
             polylines_out.pop_back();
-        intrsctn = NULL;
-        i_intersection = -1;
-        polyline_current = NULL;
+        it 				 = nullptr;
+        i_intersection   = -1;
+        polyline_current = nullptr;
     }
 }
 
-struct MonotonousRegion;
-
-struct NextMonotonousRegion
-{
-	MonotonousRegion *region;
-	struct Path {
-		float length { 0 }; 		// Length of the link to the next region.
-		float visibility { 0 }; 	// 1 / length. Which length, just to the next region, or including the path accross the region?
-		float pheromone { 0 }; 		// <0, 1>
-	};
-	enum Index : int {
-		LowLow,
-		LowHigh,
-		HighLow,
-		HighHigh
-	};
-	Path paths[4];
-};
-
 struct MonotonousRegion
 {
     struct Boundary {
@@ -1565,20 +1541,28 @@ struct MonotonousRegion
     Boundary 	right;
 
     // Length when starting at left.low
-    double 		len1;
+    float 		len1 { 0.f };
     // Length when starting at left.high
-    double 		len2;
+    float 		len2 { 0.f };
     // If true, then when starting at left.low, then ending at right.high and vice versa.
     // If false, then ending at the same side as starting.
-    bool 		flips;
+    bool 		flips { false };
 
+    bool        length(bool region_flipped) const { return region_flipped ? len2 : len1; }
     int 		left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; }
     int 		right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; }
 
     // Left regions are used to track whether all regions left to this one have already been printed.
     boost::container::small_vector<MonotonousRegion*, 4>	left_neighbors;
     // Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics.
-    boost::container::small_vector<NextMonotonousRegion, 4>	right_neighbors;
+    boost::container::small_vector<MonotonousRegion*, 4>	right_neighbors;
+};
+
+struct AntPath
+{
+	float length 	 { -1. }; 		// Length of the link to the next region.
+	float visibility { -1. }; 		// 1 / length. Which length, just to the next region, or including the path accross the region?
+	float pheromone  { 0 }; 		// <0, 1>
 };
 
 struct MonotonousRegionLink
@@ -1587,10 +1571,65 @@ struct MonotonousRegionLink
     bool 				 flipped;
     // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region 
     // is applied as defined.
-    NextMonotonousRegion::Path *next;
+    AntPath 			*next;
     // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region
     // is applied in reverse order as if the zig-zags were flipped.
-    NextMonotonousRegion::Path *next_flipped;
+    AntPath 			*next_flipped;
+};
+
+class AntPathMatrix
+{
+public:
+	AntPathMatrix(const std::vector<MonotonousRegion> &regions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs) : 
+		m_regions(regions),
+		m_poly_with_offset(poly_with_offset),
+		m_segs(segs),
+		// From end of one region to the start of another region, both flipped or not flipped.
+		m_matrix(regions.size() * regions.size() * 4) {}
+
+	AntPath& operator()(const MonotonousRegion &region_from, bool flipped_from, const MonotonousRegion &region_to, bool flipped_to)
+	{
+		int row = 2 * int(&region_from - m_regions.data()) + flipped_from;
+		int col = 2 * int(&region_to   - m_regions.data()) + flipped_to;
+		AntPath &path = m_matrix[row * m_regions.size() * 2 + col];
+		if (path.length == -1.) {
+			// This path is accessed for the first time. Update the length and cost.
+			int i_from = region_from.right_intersection_point(flipped_from);
+			int i_to   = region_to.left_intersection_point(flipped_to);
+			const SegmentedIntersectionLine &vline_from = m_segs[region_from.right.vline];
+			const SegmentedIntersectionLine &vline_to   = m_segs[region_to.left.vline];
+			if (region_from.right.vline + 1 == region_from.left.vline) {
+				int i_right = vline_from.intersections[i_from].right_horizontal();
+				if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
+					// Measure length along the contour.
+	                path.length = measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to);
+				}
+			}
+			if (path.length == -1.) {
+				// Just apply the Eucledian distance of the end points.
+			    path.length = Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm();
+			}
+			path.visibility = 1. / (path.length + EPSILON);
+		}
+		return path;
+	}
+
+	AntPath& operator()(const MonotonousRegionLink &region_from, const MonotonousRegion &region_to, bool flipped_to)
+		{ return (*this)(*region_from.region, region_from.flipped, region_to, flipped_to); }
+	AntPath& operator()(const MonotonousRegion &region_from, bool flipped_from, const MonotonousRegionLink &region_to)
+		{ return (*this)(region_from, flipped_from, *region_to.region, region_to.flipped); }
+    AntPath& operator()(const MonotonousRegionLink &region_from, const MonotonousRegionLink &region_to)
+        { return (*this)(*region_from.region, region_from.flipped, *region_to.region, region_to.flipped); }
+
+private:
+	// Source regions, used for addressing and updating m_matrix.
+	const std::vector<MonotonousRegion>    			&m_regions;
+	// To calculate the intersection points and contour lengths.
+	const ExPolygonWithOffset 						&m_poly_with_offset;
+	const std::vector<SegmentedIntersectionLine> 	&m_segs;
+	// From end of one region to the start of another region, both flipped or not flipped.
+	//FIXME one may possibly use sparse representation of the matrix.
+	std::vector<AntPath>					         m_matrix;
 };
 
 static const SegmentIntersection& vertical_run_bottom(const SegmentedIntersectionLine &vline, const SegmentIntersection &start)
@@ -1601,10 +1640,15 @@ static const SegmentIntersection& vertical_run_bottom(const SegmentedIntersectio
 	for (;;) {
 		while (it->type != SegmentIntersection::INNER_LOW)
 			-- it;
-		int down = it->vertical_down();
-		if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid)
-			break;
-		it = &vline.intersections[down];
+        if ((it - 1)->type == SegmentIntersection::INNER_HIGH)
+            -- it;
+        else {
+            int down = it->vertical_down();
+            if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid)
+                break;
+            it = &vline.intersections[down];
+            assert(it->type == SegmentIntersection::INNER_HIGH);
+        }
 	}
 	return *it;
 }
@@ -1621,10 +1665,15 @@ static const SegmentIntersection& vertical_run_top(const SegmentedIntersectionLi
 	for (;;) {
 		while (it->type != SegmentIntersection::INNER_HIGH)
 			++ it;
-		int up = it->vertical_up();
-		if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid)
-			break;
-		it = &vline.intersections[up];
+        if ((it + 1)->type == SegmentIntersection::INNER_LOW)
+            ++ it;
+        else {
+            int up = it->vertical_up();
+            if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid)
+                break;
+            it = &vline.intersections[up];
+            assert(it->type == SegmentIntersection::INNER_LOW);
+        }
 	}
 	return *it;
 }
@@ -1719,41 +1768,53 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
 {
 	std::vector<MonotonousRegion> monotonous_regions;
 
-    for (size_t i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) {
+    for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) {
         SegmentedIntersectionLine  &vline_seed = segs[i_vline_seed];
-    	for (size_t i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) {
+    	for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) {
 	        while (i_intersection_seed + 1 < vline_seed.intersections.size() &&
 	        	   vline_seed.intersections[i_intersection_seed].type != SegmentIntersection::INNER_LOW)
 	        	++ i_intersection_seed;
 			SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed];
-            SegmentIntersection *end   = &end_of_vertical_run_raw(*start);
+            SegmentIntersection *end   = &end_of_vertical_run(vline_seed, *start);
 			if (! start->consumed_vertical_up) {
 				// Draw a new monotonous region starting with this segment.
 				// while there is only a single right neighbor
-				start->consumed_vertical_up = true;
-		        size_t i_vline = i_vline_seed;
+		        int i_vline = i_vline_seed;
                 std::pair<SegmentIntersection*, SegmentIntersection*> left(start, end);
 				MonotonousRegion region;
 				region.left.vline = i_vline;
-				region.left.low   = left.first  - vline_seed.intersections.data();
-				region.left.high  = left.second - vline_seed.intersections.data();
+				region.left.low   = int(left.first  - vline_seed.intersections.data());
+				region.left.high  = int(left.second - vline_seed.intersections.data());
 				region.right      = region.left;
+				start->consumed_vertical_up = true;
+				int num_lines = 1;
 				while (++ i_vline < segs.size()) {
 			        SegmentedIntersectionLine  &vline_left	= segs[i_vline - 1];
 			        SegmentedIntersectionLine  &vline_right = segs[i_vline];
-					std::pair<SegmentIntersection*, SegmentIntersection*> right 	  = right_overlap(left, vline_right);
-					std::pair<SegmentIntersection*, SegmentIntersection*> right_left  = left_overlap(right, vline_left);
+					std::pair<SegmentIntersection*, SegmentIntersection*> right 	      = right_overlap(left, vline_right);
+                    if (right.first == nullptr)
+                        // No neighbor at the right side of the current segment.
+                        break;
+                    SegmentIntersection*                                  right_top_first = &vertical_run_top(vline_right, *right.first);
+                    if (right_top_first != right.second)
+                        // This segment overlaps with multiple segments at its right side.
+                        break;
+                    std::pair<SegmentIntersection*, SegmentIntersection*> right_left  = left_overlap(right, vline_left);
 					if (left != right_left)
-						// Left & right draws don't overlap exclusively.
+						// Left & right draws don't overlap exclusively, right neighbor segment overlaps with multiple segments at its left.
 						break;
 					region.right.vline = i_vline;
-					region.right.low   = right.first  - vline_right.intersections.data();
-					region.right.high  = right.second - vline_right.intersections.data();
+					region.right.low   = int(right.first  - vline_right.intersections.data());
+					region.right.high  = int(right.second - vline_right.intersections.data());
 					right.first->consumed_vertical_up = true;
+					++ num_lines;
 					left = right;
 				}
+				// Even number of lines makes the infill zig-zag to exit on the other side of the region than where it starts.
+				region.flips = (num_lines & 1) != 0;
+                monotonous_regions.emplace_back(region);
 			}
-			i_intersection_seed = end - vline_seed.intersections.data() + 1;
+			i_intersection_seed = int(end - vline_seed.intersections.data()) + 1;
 		}
     }
 
@@ -1781,38 +1842,41 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> &regions, s
 	for (MonotonousRegion &region : regions) {
 		if (region.left.vline > 0) {
 			auto &vline = segs[region.left.vline];
-			auto  begin = &vline.intersections[region.left.low];
-			auto  end   = &vline.intersections[region.left.high];
-            for (;;) {
-                MapType key(begin, nullptr);
-                auto it = std::lower_bound(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), key);
-                assert(it != map_intersection_to_region_end.end() && it->first == key.first);
-                NextMonotonousRegion next_region{ &region };
-				it->second->right_neighbors.emplace_back(next_region);
-				SegmentIntersection *next = &vertical_run_top(vline, *begin);
-				if (next == end)
-					break;
-				while (next->type != SegmentIntersection::INNER_LOW)
-					++ next;
-				begin = next;
-			}
+            auto &vline_left = segs[region.left.vline - 1];
+            auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline_left);
+            if (lbegin != nullptr) {
+                for (;;) {
+                    MapType key(lbegin, nullptr);
+                    auto it = std::lower_bound(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), key);
+                    assert(it != map_intersection_to_region_end.end() && it->first == key.first);
+				    it->second->right_neighbors.emplace_back(&region);
+				    SegmentIntersection *lnext = &vertical_run_top(vline_left, *lbegin);
+				    if (lnext == lend)
+					    break;
+				    while (lnext->type != SegmentIntersection::INNER_LOW)
+					    ++ lnext;
+				    lbegin = lnext;
+			    }
+            }
 		}
 		if (region.right.vline + 1 < segs.size()) {
 			auto &vline = segs[region.right.vline];
-			auto  begin = &vline.intersections[region.right.low];
-			auto  end   = &vline.intersections[region.right.high];
-			for (;;) {
-				MapType key(begin, nullptr);
-				auto it = std::lower_bound(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), key);
-				assert(it != map_intersection_to_region_start.end() && it->first == key.first);
-				it->second->left_neighbors.emplace_back(&region);
-				SegmentIntersection *next = &vertical_run_top(vline, *begin);
-				if (next == end)
-					break;
-				while (next->type != SegmentIntersection::INNER_LOW)
-					++ next;
-				begin = next;
-			}
+            auto &vline_right = segs[region.right.vline + 1];
+            auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline_right);
+            if (rbegin != nullptr) {
+			    for (;;) {
+				    MapType key(rbegin, nullptr);
+				    auto it = std::lower_bound(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), key);
+				    assert(it != map_intersection_to_region_start.end() && it->first == key.first);
+				    it->second->left_neighbors.emplace_back(&region);
+				    SegmentIntersection *rnext = &vertical_run_top(vline_right, *rbegin);
+				    if (rnext == rend)
+					    break;
+				    while (rnext->type != SegmentIntersection::INNER_LOW)
+					    ++ rnext;
+				    rbegin = rnext;
+			    }
+            }
 		}
 	}
 }
@@ -1821,7 +1885,7 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> &regions, s
 // https://www.chalmers.se/en/departments/math/research/research-groups/optimization/OptimizationMasterTheses/MScThesis-RaadSalman-final.pdf
 // Algorithm 6.1 Lexicographic Path Preserving 3-opt
 // Optimize path while maintaining the ordering constraints.
-void monotonous_3_opt(std::vector<MonotonousRegionLink> &path, std::vector<SegmentedIntersectionLine> &segs)
+void monotonous_3_opt(std::vector<MonotonousRegionLink> &path, const std::vector<SegmentedIntersectionLine> &segs)
 {
 	// When doing the 3-opt path preserving flips, one has to fulfill two constraints:
 	//
@@ -1842,19 +1906,19 @@ void monotonous_3_opt(std::vector<MonotonousRegionLink> &path, std::vector<Segme
 
 // Find a run through monotonous infill blocks using an 'Ant colony" optimization method.
 static std::vector<MonotonousRegionLink> chain_monotonous_regions(
-	std::vector<MonotonousRegion> &regions, std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
+	std::vector<MonotonousRegion> &regions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
 {
 	// Start point of a region (left) given the direction of the initial infill line.
 	auto region_start_point = [&segs](const MonotonousRegion &region, bool dir) {
-		SegmentedIntersectionLine 	&vline  = segs[region.left.vline];
-		SegmentIntersection      	&ipt    = vline.intersections[dir ? region.left.high : region.left.low];
+		const SegmentedIntersectionLine &vline  = segs[region.left.vline];
+        const SegmentIntersection      	&ipt    = vline.intersections[dir ? region.left.high : region.left.low];
 		return Vec2f(float(vline.pos), float(ipt.pos()));
 	};
 	// End point of a region (right) given the direction of the initial infill line and whether the monotonous run contains
 	// even or odd number of vertical lines.
     auto region_end_point = [&segs](const MonotonousRegion &region, bool dir) {
-		SegmentedIntersectionLine 	&vline  = segs[region.right.vline];
-		SegmentIntersection      	&ipt    = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high];
+        const SegmentedIntersectionLine	&vline  = segs[region.right.vline];
+        const SegmentIntersection      	&ipt    = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high];
 		return Vec2f(float(vline.pos), float(ipt.pos()));
 	};
 
@@ -1867,7 +1931,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 		if (region.left_neighbors.empty())
 			queue.emplace_back(&region);
 		else
-			left_neighbors_unprocessed[&region - regions.data()] = region.left_neighbors.size();
+			left_neighbors_unprocessed[&region - regions.data()] = int(region.left_neighbors.size());
 	// Make copy of structures that need to be initialized at each ant iteration.
 	auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed;
 	auto queue_initial 						= queue;
@@ -1878,14 +1942,16 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 	float best_path_length = std::numeric_limits<float>::max();
 
 	struct NextCandidate {
-        NextMonotonousRegion        *region;
-        NextMonotonousRegion::Path  *link;
-        NextMonotonousRegion::Path  *link_flipped;
-        float                        cost;
-		bool 			             dir;
+        MonotonousRegion    *region;
+        AntPath  	        *link;
+        AntPath  	        *link_flipped;
+        float                cost;
+		bool 		         dir;
 	};
 	std::vector<NextCandidate> next_candidates;
 
+	AntPathMatrix path_matrix(regions, poly_with_offset, segs);
+
 	// How many times to repeat the ant simulation.
 	constexpr int num_runs = 10;
 	// With how many ants each of the run will be performed?
@@ -1900,10 +1966,10 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 	constexpr float pheromone_alpha = 1.f; // pheromone exponent
 	constexpr float pheromone_beta  = 2.f; // attractiveness weighted towards edge length
 	// Cost of traversing a link between two monotonous regions.
-	auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path &path) {
+	auto path_cost = [pheromone_alpha, pheromone_beta](AntPath &path) {
 		return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
 	};
-	for (int run = 0; run < num_runs; ++ run)
+    for (int run = 0; run < num_runs; ++ run)
 	{
 		for (int ant = 0; ant < num_ants; ++ ant) 
 		{
@@ -1911,92 +1977,111 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 			path.clear();
 			queue = queue_initial;
 			left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
-			while (! queue.empty()) {
-				// Sort the queue by distance to the last point.
-				// Take a candidate based on shortest distance? or ant colony?
-				if (path.empty()) {
-					// Pick randomly the first from the queue at random orientation.
-					int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng);
-                    path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 });
-					*(queue.begin() + first_idx) = std::move(queue.back());
-					queue.pop_back();
+            // Pick randomly the first from the queue at random orientation.
+            int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng);
+            path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 });
+            *(queue.begin() + first_idx) = std::move(queue.back());
+            queue.pop_back();
+            assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0);
+
+			while (! queue.empty() || ! path.back().region->right_neighbors.empty()) {
+				// Chain.
+				MonotonousRegion 		    &region = *path.back().region;
+				bool 			  			 dir    = path.back().flipped;
+				Vec2f                    	 end_pt	= region_end_point(region, dir);
+				// Sort by distance to pt.
+                next_candidates.clear();
+				next_candidates.reserve(region.right_neighbors.size() * 2);
+				for (MonotonousRegion *next : region.right_neighbors) {
+					int &unprocessed = left_neighbors_unprocessed[next - regions.data()];
+					assert(unprocessed > 0);
+					if (-- unprocessed == 0) {
+						// Dependencies of the successive blocks are satisfied.
+                        AntPath &path1  	   = path_matrix(region,   dir, *next, false);
+                        AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
+                        AntPath &path2 	       = path_matrix(region,   dir, *next, true);
+                        AntPath &path2_flipped = path_matrix(region, ! dir, *next, false);
+                        next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false });
+                        next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true  });
+					}
+				}
+                size_t num_direct_neighbors = next_candidates.size();
+                //FIXME add the queue items to the candidates? These are valid moves as well.
+                if (num_direct_neighbors == 0) {
+                    // Add the queue candidates.
+                    for (MonotonousRegion *next : queue) {
+                        AntPath &path1  	   = path_matrix(region,   dir, *next, false);
+                        AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
+                        AntPath &path2 	       = path_matrix(region,   dir, *next, true);
+                        AntPath &path2_flipped = path_matrix(region, ! dir, *next, false);
+                        next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false });
+                        next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true  });
+                    }
+                }
+				float dice = float(rng()) / float(rng.max());
+                std::vector<NextCandidate>::iterator take_path;
+				if (dice < probability_take_best) {
+					// Take the lowest cost path.
+					take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; });
 				} else {
-					// Pick the closest neighbor from the queue?
-				}
-				-- left_neighbors_unprocessed[path.back().region - regions.data()];
-				while (! path.back().region->right_neighbors.empty()) {
-					// Chain.
-					MonotonousRegion 		    &region = *path.back().region;
-					bool 			  			 dir    = path.back().flipped;
-					Vec2f                    	 end_pt	= region_end_point(region, dir);
-					// Sort by distance to pt.
-					next_candidates.reserve(region.right_neighbors.size() * 2);
-					for (NextMonotonousRegion &next : region.right_neighbors) {
-						int unprocessed = left_neighbors_unprocessed[next.region - regions.data()];
-						assert(unprocessed > 0);
-						if (unprocessed == 1) {
-							// Dependencies of the successive blocks are satisfied.
-                            bool flip = dir == region.flips;
-                            auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path& path) {
-                                return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
-                            };
-                            NextMonotonousRegion::Path &path_low  		  = next.paths[flip ? NextMonotonousRegion::HighLow  : NextMonotonousRegion::LowLow];
-                            NextMonotonousRegion::Path &path_low_flipped  = next.paths[flip ? NextMonotonousRegion::LowHigh  : NextMonotonousRegion::HighHigh];
-                            NextMonotonousRegion::Path &path_high 	      = next.paths[flip ? NextMonotonousRegion::HighHigh : NextMonotonousRegion::LowHigh];
-                            NextMonotonousRegion::Path &path_high_flipped = next.paths[flip ? NextMonotonousRegion::LowLow   : NextMonotonousRegion::HighLow];
-                            next_candidates.emplace_back(NextCandidate{ &next, &path_low,  &path_low_flipped,  path_cost(path_low),  false });
-                            next_candidates.emplace_back(NextCandidate{ &next, &path_high, &path_high_flipped, path_cost(path_high), true });
-						}
-					}
-					//std::sort(next_candidates.begin(), next_candidates.end(), [](const auto &l, const auto &r) { l.dist < r.dist; });
-					float dice = float(rng()) / float(rng.max());
-                    std::vector<NextCandidate>::iterator take_path;
-					if (dice < probability_take_best) {
-						// Take the lowest cost path.
-						take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; });
-					} else {
-						// Take the path based on the cost.
-                        // Calculate the total cost.
-                        float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; });
-						// Take a random path based on the cost.
-                        float cost_threshold = floor(float(rng()) * total_cost / float(rng.max()));
-                        take_path = next_candidates.end();
-                        -- take_path;
-                        for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it)
-                            if (cost_threshold -= it->cost <= 0.) {
-                                take_path = it;
-                                break;
-                            }
-					}
-					// Extend the path.
-					NextMonotonousRegion &next_region = *take_path->region;
-					bool        		  next_dir    = take_path->dir;
-                    path.back().next         = take_path->link;
-                    path.back().next_flipped = take_path->link_flipped;
-                    path.emplace_back(MonotonousRegionLink{ next_region.region, next_dir });
-					// Decrease the number of next block dependencies.
-					-- left_neighbors_unprocessed[next_region.region - regions.data()];
-					// Update pheromones along this link.
-					take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit;
+					// Take the path based on the cost.
+                    // Calculate the total cost.
+                    float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; });
+					// Take a random path based on the cost.
+                    float cost_threshold = floor(float(rng()) * total_cost / float(rng.max()));
+                    take_path = next_candidates.end();
+                    -- take_path;
+                    for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it)
+                        if (cost_threshold -= it->cost <= 0.) {
+                            take_path = it;
+                            break;
+                        }
 				}
+                // Move the other right neighbors with satisified constraints to the queue.
+                bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors;
+                for (std::vector<NextCandidate>::iterator it_next_candidate = next_candidates.begin(); it_next_candidate != next_candidates.begin() + num_direct_neighbors; ++ it_next_candidate)
+                    if ((queue.empty() || it_next_candidate->region != queue.back()) && it_next_candidate->region != take_path->region)
+                        queue.emplace_back(it_next_candidate->region);
+                if (take_path - next_candidates.begin() >= num_direct_neighbors) {
+                    // Remove the selected path from the queue.
+                    auto it = std::find(queue.begin(), queue.end(), take_path->region);
+                    *it = queue.back();
+                    queue.pop_back();
+                }
+				// Extend the path.
+				MonotonousRegion *next_region = take_path->region;
+				bool              next_dir    = take_path->dir;
+                path.back().next         = take_path->link;
+                path.back().next_flipped = take_path->link_flipped;
+                path.emplace_back(MonotonousRegionLink{ next_region, next_dir });
+				// Update pheromones along this link.
+				take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit;
 			}
 
 			// Perform 3-opt local optimization of the path.
 			monotonous_3_opt(path, segs);
 
 			// Measure path length.
-            float path_length = std::accumulate(path.begin(), path.end(), 0.f, [](const float l, const MonotonousRegionLink& r) { return l + r.next->length; });
+            assert(! path.empty());
+            float path_length = std::accumulate(path.begin(), path.end() - 1,
+                path.back().region->length(path.back().flipped),
+                [&path_matrix](const float l, const MonotonousRegionLink &r) { 
+                    const MonotonousRegionLink &next = *(&r + 1);
+                    return l + r.region->length(r.flipped) + path_matrix(*r.region, r.flipped, *next.region, next.flipped).length;
+                });
 			// Save the shortest path.
 			if (path_length < best_path_length) {
 				best_path_length = path_length;
-				std::swap(best_path_length, path_length);
+				std::swap(best_path, path);
 			}
 		}
 
 		// Reinforce the path feromones with the best path.
-        float total_cost = best_path_length;
-		for (MonotonousRegionLink &link : path)
+        float total_cost = best_path_length + EPSILON;
+        for (size_t i = 0; i + 1 < path.size(); ++ i) {
+            MonotonousRegionLink &link = path[i];
             link.next->pheromone = (1.f - pheromone_evaporation) * link.next->pheromone + pheromone_evaporation / total_cost;
+        }
 	}
 
 	return best_path;
@@ -2055,76 +2140,76 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
         }
 
 		for (;;) {
-	        const SegmentedIntersectionLine &seg = segs[i_vline];
-            const SegmentIntersection       *intrsctn = &seg.intersections[i_intersection];
-            const bool                       going_up = intrsctn->is_low();
+	        const SegmentedIntersectionLine &vline = segs[i_vline];
+            const SegmentIntersection       *it    = &vline.intersections[i_intersection];
+            const bool                       going_up = it->is_low();
             if (polyline == nullptr) {
 				polylines_out.emplace_back();
 	            polyline = &polylines_out.back();
 	            // Extend the infill line up to the outer contour.
-	        	polyline->points.emplace_back(seg.pos, (intrsctn + (going_up ? - 1 : 1))->pos());
+	        	polyline->points.emplace_back(vline.pos, (it + (going_up ? - 1 : 1))->pos());
 			} else
-				polyline->points.emplace_back(seg.pos, intrsctn->pos());
+				polyline->points.emplace_back(vline.pos, it->pos());
 
-			int iright = intrsctn->right_horizontal();
+			int iright = it->right_horizontal();
 	        if (going_up) {
 	            // Consume the complete vertical segment up to the inner contour.
 	            for (;;) {
 		            do {
-		                ++ intrsctn;
-						iright = std::max(iright, intrsctn->right_horizontal());
-		            } while (intrsctn->type != SegmentIntersection::INNER_HIGH);
-	                polyline->points.emplace_back(seg.pos, intrsctn->pos());
-		            int inext = intrsctn->vertical_up();
+		                ++ it;
+						iright = std::max(iright, it->right_horizontal());
+		            } while (it->type != SegmentIntersection::INNER_HIGH);
+	                polyline->points.emplace_back(vline.pos, it->pos());
+		            int inext = it->vertical_up();
 		            if (inext == -1)
 		            	break;
-		            const Polygon &poly = poly_with_offset.contour(intrsctn->iContour);
-	                assert(intrsctn->iContour == seg.intersections[inext].iContour);
-	                emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline, intrsctn->has_right_vertical_up());
-	                intrsctn = seg.intersections.data() + inext;
+		            const Polygon &poly = poly_with_offset.contour(it->iContour);
+	                assert(it->iContour == vline.intersections[inext].iContour);
+	                emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_left_vertical_up());
+	                it = vline.intersections.data() + inext;
 	            } 
 	        } else {
 	            // Going down.
-	            assert(intrsctn->is_high());
+	            assert(it->is_high());
 	            assert(i_intersection > 0);
 	            for (;;) {
 		            do {
-		                -- intrsctn;
-		                if (int iright_new = intrsctn->right_horizontal(); iright_new != -1)
+		                -- it;
+		                if (int iright_new = it->right_horizontal(); iright_new != -1)
 		                	iright = iright_new;
-		            } while (intrsctn->type != SegmentIntersection::INNER_LOW);
-	                polyline->points.emplace_back(seg.pos, intrsctn->pos());
-		            int inext = intrsctn->vertical_down();
+		            } while (it->type != SegmentIntersection::INNER_LOW);
+	                polyline->points.emplace_back(vline.pos, it->pos());
+		            int inext = it->vertical_down();
 		            if (inext == -1)
 		            	break;
-		            const Polygon &poly = poly_with_offset.contour(intrsctn->iContour);
-	                assert(intrsctn->iContour == seg.intersections[inext].iContour);
-	                emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, intrsctn->has_right_vertical_down());
-	                intrsctn = seg.intersections.data() + inext;
+		            const Polygon &poly = poly_with_offset.contour(it->iContour);
+	                assert(it->iContour == vline.intersections[inext].iContour);
+	                emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_right_vertical_down());
+	                it = vline.intersections.data() + inext;
 	            } 
 	        }
 
 	        if (i_vline == region.right.vline)
 	        	break;
 
-	        int inext = intrsctn->right_horizontal();
-	        if (inext != -1 && intrsctn->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
+	        int inext = it->right_horizontal();
+	        if (inext != -1 && it->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
 	        	// Emit a horizontal connection contour.
-	            emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, true);
+	            emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, true);
 	            i_intersection = inext;
 	        } else {
 		        // Finish the current vertical line,
-	        	going_up ? ++ intrsctn : -- intrsctn;
-		        assert(intrsctn->is_outer());
-		        assert(intrsctn->is_high() == going_up);
-	        	polyline->points.back() = Point(seg.pos, intrsctn->pos());
+	        	going_up ? ++ it : -- it;
+		        assert(it->is_outer());
+		        assert(it->is_high() == going_up);
+	        	polyline->points.back() = Point(vline.pos, it->pos());
 				finish_polyline();
 				if (inext == -1) {
 					// Find the end of the next overlapping vertical segment.
 			        const SegmentedIntersectionLine &vline_right = segs[i_vline + 1];
                     const SegmentIntersection       *right       = going_up ? 
                         &vertical_run_top(vline_right, vline_right.intersections[iright]) : &vertical_run_bottom(vline_right, vline_right.intersections[iright]);
-					i_intersection = right - vline_right.intersections.data();
+					i_intersection = int(right - vline_right.intersections.data());
 				} else
 		            i_intersection = inext;
 	        }
@@ -2132,6 +2217,18 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
 	        ++ i_vline;
 	    }
     }
+
+    if (polyline != nullptr) {
+        // Finish the current vertical line,
+        const MonotonousRegion           &region = *path.back().region;
+        const SegmentedIntersectionLine  &vline  = segs[region.right.vline];
+        const SegmentIntersection        *ip     = &vline.intersections[region.right_intersection_point(path.back().flipped)];
+        assert(ip->is_inner());
+        ip->is_low() ? -- ip : ++ ip;
+        assert(ip->is_outer());
+        polyline->points.back() = Point(vline.pos, ip->pos());
+        finish_polyline();
+    }
 }
 
 bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams &params, float angleBase, float pattern_shift, Polylines &polylines_out)
@@ -2224,13 +2321,16 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
     svg.Close();
 #endif /* SLIC3R_DEBUG */
 
+    //FIXME this is a hack to get the monotonous infill rolling. We likely want a smarter switch, likely based on user decison.
     bool monotonous_infill = params.density > 0.99;
     if (monotonous_infill) {
 		std::vector<MonotonousRegion> regions = generate_montonous_regions(segs);
 		connect_monotonous_regions(regions, segs);
-		std::mt19937_64 rng;
-		std::vector<MonotonousRegionLink> path = chain_monotonous_regions(regions, segs, rng);
-		polylines_from_paths(path, poly_with_offset, segs, polylines_out);
+        if (! regions.empty()) {
+		    std::mt19937_64 rng;
+		    std::vector<MonotonousRegionLink> path = chain_monotonous_regions(regions, poly_with_offset, segs, rng);
+		    polylines_from_paths(path, poly_with_offset, segs, polylines_out);
+        }
 	} else
 		traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out);
 
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 01a804ee7..b5890f578 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -3439,10 +3439,13 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr
 
     // First we append the entities, there are eec->entities.size() of them:
     size_t old_size = perimeters_or_infills->size();
-    size_t new_size = old_size + eec->entities.size();
+    size_t new_size = old_size + (eec->can_reverse() ? eec->entities.size() : 1);
     perimeters_or_infills->reserve(new_size);
-    for (auto* ee : eec->entities)
-        perimeters_or_infills->emplace_back(ee);
+    if (eec->can_reverse()) {
+	    for (auto* ee : eec->entities)
+	        perimeters_or_infills->emplace_back(ee);
+	} else
+		perimeters_or_infills->emplace_back(const_cast<ExtrusionEntityCollection*>(eec));
 
     if (copies_extruder != nullptr) {
     	// Don't reallocate overrides if not needed.

From f9b3f2d45ed22dc506fa7a43cf86563b1e6710c8 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 24 Apr 2020 11:01:14 +0200
Subject: [PATCH 37/55] Search: Code cleaning

+ Reverted "Plater" tab to the Settings Notepad
---
 src/slic3r/GUI/GLCanvas3D.cpp   |  12 ++--
 src/slic3r/GUI/ImGuiWrapper.cpp |   6 +-
 src/slic3r/GUI/ImGuiWrapper.hpp |   2 +-
 src/slic3r/GUI/MainFrame.cpp    |  13 ++--
 src/slic3r/GUI/Plater.cpp       |  18 +----
 src/slic3r/GUI/Plater.hpp       |   3 +-
 src/slic3r/GUI/Search.cpp       | 119 +++-----------------------------
 src/slic3r/GUI/Search.hpp       |  27 +-------
 src/slic3r/GUI/Tab.cpp          |   7 --
 src/slic3r/GUI/Tab.hpp          |   3 -
 10 files changed, 34 insertions(+), 176 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 02f92168c..1fdeba700 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4519,18 +4519,15 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
 
     imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, 
                        sidebar.get_searcher().view_params,
-                       selected, edited, check_changed);
+                       selected, edited);
 
     search_line = s;
     delete [] s;
 
-    if (edited)
-        sidebar.search_and_apply_tab_search_lines();
-
-    if (check_changed) {
+    if (edited) {
         if (search_line == _u8L("Type here to search"))
             search_line.clear();
-        sidebar.search_and_apply_tab_search_lines(true);
+        sidebar.search();
     }
 
     if (selected != size_t(-1))
@@ -5261,6 +5258,9 @@ bool GLCanvas3D::_init_collapse_toolbar()
     if (!m_collapse_toolbar.add_item(item))
         return false;
 
+    if (!m_collapse_toolbar.add_separator())
+        return false;
+
     item.name = "print";
     item.icon_filename = "cog.svg";
     item.tooltip = _utf8(L("Switch to Print Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "2]";
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index f9437ed61..bbe7c088b 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -602,7 +602,7 @@ static void process_key_down(ImGuiKey imgui_key, std::function<void()> f)
 }
 
 void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
-                               Search::OptionViewParameters& view_params, int& selected, bool& edited, bool& check_changed)
+                               Search::OptionViewParameters& view_params, int& selected, bool& edited)
 {
     // ImGui::ListBoxHeader("", size);
     {   
@@ -707,13 +707,13 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
 
     ImGui::ListBoxFooter();
 
-    auto check_box = [&check_changed, this](const wxString& label, bool& check) {
+    auto check_box = [&edited, this](const wxString& label, bool& check) {
         ImGui::SameLine();
         bool ch = check;
         checkbox(label, ch);
         if (ImGui::IsItemClicked()) {
             check = !check;
-            check_changed = true;
+            edited = true;
         }
     };
 
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index d32604963..a5195ad39 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -79,7 +79,7 @@ public:
     bool combo(const wxString& label, const std::vector<std::string>& options, int& selection);   // Use -1 to not mark any option as selected
     bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
     void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
-                     Search::OptionViewParameters& view_params, int& selected, bool& edited, bool& check_changed);
+                     Search::OptionViewParameters& view_params, int& selected, bool& edited);
 
     void disabled_begin(bool disabled);
     void disabled_end();
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 48c5fe99b..7f4dbf861 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -306,6 +306,8 @@ void MainFrame::init_tabpanel()
             // before the MainFrame is fully set up.
             static_cast<Tab*>(panel)->OnActivate();
         }
+        else
+            select_tab(0);
     });
 
 //!    m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
@@ -313,6 +315,7 @@ void MainFrame::init_tabpanel()
 
     wxGetApp().plater_ = m_plater;
 //    m_tabpanel->AddPage(m_plater, _(L("Plater")));
+    m_tabpanel->AddPage(new wxPanel(m_tabpanel), _L("Plater")); // empty panel just for Plater tab
 
     wxGetApp().obj_list()->create_popup_menus();
 
@@ -756,7 +759,7 @@ void MainFrame::init_menubar()
 //!        size_t tab_offset = 0;
         if (m_plater) {
             append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")),
-                [this/*, tab_offset*/](wxCommandEvent&) { select_tab((size_t)(-1)); }, "plater", nullptr,
+                [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*(size_t)(-1)*/0); }, "plater", nullptr,
                 [this]() {return true; }, this);
 //!            tab_offset += 1;
 //!        }
@@ -764,14 +767,14 @@ void MainFrame::init_menubar()
             windowMenu->AppendSeparator();
         }
         append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")),
-            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + */0); }, "cog", nullptr,
+            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 0*/1); }, "cog", nullptr,
             [this]() {return true; }, this);
         wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")),
-            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + */1); }, "spool", nullptr,
+            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 1*/2); }, "spool", nullptr,
             [this]() {return true; }, this);
         m_changeable_menu_items.push_back(item_material_tab);
         wxMenuItem* item_printer_tab = append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")),
-            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + */2); }, "printer", nullptr,
+            [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 2*/3); }, "printer", nullptr,
             [this]() {return true; }, this);
         m_changeable_menu_items.push_back(item_printer_tab);
         if (m_plater) {
@@ -1237,7 +1240,7 @@ void MainFrame::load_config(const DynamicPrintConfig& config)
 
 void MainFrame::select_tab(size_t tab)
 {
-    if (tab == (size_t)(-1)) {
+    if (tab == /*(size_t)(-1)*/0) {
         if (m_plater && !m_plater->IsShown())
             this->switch_to(true);
     }
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 149bc95ae..fc87ab00d 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -726,7 +726,6 @@ struct Sidebar::priv
 
     bool                is_collapsed {false};
     Search::OptionsSearcher     searcher;
-    std::string                 search_line;
 
     priv(Plater *plater) : plater(plater) {}
     ~priv();
@@ -1096,10 +1095,9 @@ void Sidebar::msw_rescale()
     p->scrolled->Layout();
 }
 
-void Sidebar::search_and_apply_tab_search_lines(bool force/* = false*/)
+void Sidebar::search()
 {
-    if (p->searcher.search(p->search_line, force))
-        ;//apply_search_line_on_tabs();
+    p->searcher.search();
 }
 
 void Sidebar::jump_to_option(size_t selected)
@@ -1382,16 +1380,6 @@ static std::vector<Search::InputInfo> get_search_inputs(ConfigOptionMode mode)
     return ret;
 }
 
-void Sidebar::apply_search_line_on_tabs()
-{
-    auto& tabs_list = wxGetApp().tabs_list;
-    auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
-
-    for (auto tab : tabs_list)
-        if (tab->supports_printer_technology(print_tech))
-            tab->set_search_line(p->search_line);
-}
-
 void Sidebar::update_searcher()
 {
     p->searcher.init(get_search_inputs(m_mode));
@@ -1442,7 +1430,7 @@ Search::OptionsSearcher& Sidebar::get_searcher()
 
 std::string& Sidebar::get_search_line()
 {
-    return p->search_line;
+    return p->searcher.search_string();
 }
 
 // Plater::DropTarget
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 52903525a..b1044b1bc 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -105,7 +105,7 @@ public:
     void update_mode_sizer() const;
     void update_reslice_btn_tooltip() const;
     void msw_rescale();
-    void search_and_apply_tab_search_lines(bool force = false);
+    void search();
     void jump_to_option(size_t selected);
 
     ObjectManipulation*     obj_manipul();
@@ -132,7 +132,6 @@ public:
     void                    update_mode();
     bool                    is_collapsed();
     void                    collapse(bool collapse);
-    void                    apply_search_line_on_tabs();
     void                    update_searcher();
 
     std::vector<PresetComboBox*>&   combos_filament();
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 9ef8aa0d0..1478e725a 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -215,6 +215,11 @@ static void clear_marked_string(wxString& str)
     str.Replace(delete_string, wxEmptyString, true);
 }
 
+bool OptionsSearcher::search()
+{
+    return search(search_line, true);
+}
+
 bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
 {
     if (search_line == search && !force)
@@ -277,7 +282,9 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
     if (!full_list)
         sort_found();
 
-    search_line = search;
+    if (search_line != search)
+        search_line = search;
+
     return true;
 }
 
@@ -404,110 +411,6 @@ void SearchComboPopup::OnKeyDown(wxKeyEvent& event)
         event.Skip(); // !Needed to have EVT_CHAR generated as well
 }
 
-//------------------------------------------
-//          SearchCtrl
-//------------------------------------------
-
-SearchCtrl::SearchCtrl(wxWindow* parent) :
-    wxComboCtrl(parent, wxID_ANY, _L("Type here to search"), wxDefaultPosition, wxSize(25 * GUI::wxGetApp().em_unit(), -1), wxTE_PROCESS_ENTER)
-{
-    default_string = _L("Type here to search");
-
-    this->UseAltPopupWindow();
-
-    wxBitmap bmp_norm = create_scaled_bitmap("search_gray");
-    wxBitmap bmp_hov = create_scaled_bitmap("search");
-    this->SetButtonBitmaps(bmp_norm, true, bmp_hov, bmp_hov, bmp_norm);
-
-    popupListBox = new SearchComboPopup();
-
-    // It is important to call SetPopupControl() as soon as possible
-    this->SetPopupControl(popupListBox);
-
-    this->Bind(wxEVT_TEXT,                 &SearchCtrl::OnInputText, this);
-    this->Bind(wxEVT_TEXT_ENTER,           &SearchCtrl::PopupList, this);
-    this->Bind(wxEVT_COMBOBOX_DROPDOWN,    &SearchCtrl::PopupList, this);
-
-    this->GetTextCtrl()->Bind(wxEVT_LEFT_UP,    &SearchCtrl::OnLeftUpInTextCtrl, this);
-    popupListBox->Bind(wxEVT_LISTBOX,           &SearchCtrl::OnSelect,           this);
-}
-
-void SearchCtrl::OnInputText(wxCommandEvent& )
-{
-    if (prevent_update)
-        return;
-
-    this->GetTextCtrl()->SetInsertionPointEnd();
-
-    wxString input_string = this->GetValue();
-    if (input_string == default_string)
-        input_string.Clear();
-
-    GUI::wxGetApp().sidebar().get_search_line() = into_u8(input_string);
-
-    editing = true;
-    GUI::wxGetApp().sidebar().search_and_apply_tab_search_lines();
-    editing = false;
-}
-
-void SearchCtrl::PopupList(wxCommandEvent& e)
-{
-    update_list(GUI::wxGetApp().sidebar().get_searcher().found_options());
-    if (e.GetEventType() == wxEVT_TEXT_ENTER)
-        this->Popup();
-
-    e.Skip();
-}
-
-void SearchCtrl::set_search_line(const std::string& line)
-{
-    prevent_update = true;
-    this->SetValue(line.empty() && !editing ? default_string : from_u8(line));
-    prevent_update = false;
-}
-
-void SearchCtrl::msw_rescale()
-{
-    wxSize size = wxSize(25 * GUI::wxGetApp().em_unit(), -1);
-    // Set rescaled min height to correct layout
-    this->SetMinSize(size);
-
-    wxBitmap bmp_norm = create_scaled_bitmap("search_gray");
-    wxBitmap bmp_hov = create_scaled_bitmap("search");
-    this->SetButtonBitmaps(bmp_norm, true, bmp_hov, bmp_hov, bmp_norm);
-}
-
-void SearchCtrl::OnSelect(wxCommandEvent& event)
-{
-    int selection = event.GetSelection();
-    if (selection < 0)
-        return;
-
-    prevent_update = true;
-    GUI::wxGetApp().sidebar().jump_to_option(selection);
-    prevent_update = false;
-}
-
-void SearchCtrl::update_list(const std::vector<FoundOption>& filters)
-{
-    if (!filters.empty() && popupListBox->GetCount() == filters.size() &&
-        popupListBox->GetString(0) == filters[0].label &&
-        popupListBox->GetString(popupListBox->GetCount()-1) == filters[filters.size()-1].label)
-        return;
-
-    popupListBox->Clear();
-    for (const FoundOption& item : filters)
-        popupListBox->Append(item.label);
-}
-
-void SearchCtrl::OnLeftUpInTextCtrl(wxEvent &event)
-{
-    if (this->GetValue() == default_string)
-        this->SetValue("");
-
-    event.Skip();
-}
-
 
 //------------------------------------------
 //          SearchDialog
@@ -603,9 +506,7 @@ void SearchDialog::OnInputText(wxCommandEvent&)
     if (input_string == default_string)
         input_string.Clear();
 
-    GUI::wxGetApp().sidebar().get_search_line() = into_u8(input_string);
-
-    GUI::wxGetApp().sidebar().search_and_apply_tab_search_lines();
+    searcher->search(into_u8(input_string));
 
     update_list();
 }
@@ -684,7 +585,7 @@ void SearchDialog::OnCheck(wxCommandEvent& event)
     params.category = check_category->GetValue();
     params.group    = check_group->GetValue();
 
-    searcher->search(searcher->search_string(), true);
+    searcher->search();
     update_list();
 }
 
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index e7c1f6f58..d6e2689de 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -124,6 +124,7 @@ public:
     void apply(DynamicPrintConfig *config,
                Preset::Type        type,
                ConfigOptionMode    mode);
+    bool search();
     bool search(const std::string& search, bool force = false);
 
     void add_key(const std::string& opt_key, const wxString& group, const wxString& category);
@@ -135,7 +136,7 @@ public:
 
     const std::vector<FoundOption>& found_options() { return found; }
     const GroupAndCategory&         get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; }
-    const std::string& search_string() { return search_line; }
+    std::string& search_string() { return search_line; }
 };
 
 
@@ -171,30 +172,6 @@ protected:
     wxString m_input_string;
 };
 
-class SearchCtrl : public wxComboCtrl
-{
-    SearchComboPopup*   popupListBox {nullptr};
-
-    bool                prevent_update { false };
-    wxString            default_string;
-    bool                editing {false};
-
-    void PopupList(wxCommandEvent& event);
-    void OnInputText(wxCommandEvent& event);
-
-    void OnSelect(wxCommandEvent& event);
-    void OnLeftUpInTextCtrl(wxEvent& event);
-
-public:
-    SearchCtrl(wxWindow* parent);
-    ~SearchCtrl() {}
-
-    void		set_search_line(const std::string& search_line);
-    void        msw_rescale();
-
-    void        update_list(const std::vector<FoundOption>& filters);
-};
-
 
 //------------------------------------------
 //          SearchDialog
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index dfb2c9d4a..7b054835d 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -845,8 +845,6 @@ void Tab::msw_rescale()
     m_em_unit = wxGetApp().em_unit();
 
     m_mode_sizer->msw_rescale();
-//    m_search_cb->msw_rescale();
-    m_search->msw_rescale();
 
     m_presets_choice->SetSize(35 * m_em_unit, -1);
     m_treectrl->SetMinSize(wxSize(20 * m_em_unit, -1));
@@ -949,11 +947,6 @@ static wxString pad_combo_value_for_config(const DynamicPrintConfig &config)
     return config.opt_bool("pad_enable") ? (config.opt_bool("pad_around_object") ? _("Around object") : _("Below object")) : _("None");
 }
 
-void Tab::set_search_line(const std::string& search_line)
-{
-    m_search->set_search_line(search_line);
-}
-
 void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
 {
     if (wxGetApp().plater() == nullptr) {
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 9d91abdcf..13aee9a5f 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -122,7 +122,6 @@ protected:
 	std::string			m_name;
 	const wxString		m_title;
 	PresetBitmapComboBox*	m_presets_choice;
-	Search::SearchCtrl*	m_search;
 	ScalableButton*		m_search_btn;
 	ScalableButton*		m_btn_save_preset;
 	ScalableButton*		m_btn_delete_preset;
@@ -331,8 +330,6 @@ public:
 	PresetCollection*	get_presets() { return m_presets; }
 	size_t				get_selected_preset_item() { return m_selected_preset_item; }
 
-	void			set_search_line(const std::string& search_line);
-
 	void			on_value_change(const std::string& opt_key, const boost::any& value);
 
     void            update_wiping_button_visibility();

From 270e815eeb9ff70a44beb5e7816b71ab0480af49 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Fri, 24 Apr 2020 11:33:00 +0200
Subject: [PATCH 38/55] Fix to the last commit
 https://github.com/prusa3d/PrusaSlicer/commit/f9b3f2d45ed22dc506fa7a43cf86563b1e6710c8

---
 src/slic3r/GUI/GLCanvas3D.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 1fdeba700..95cf4921f 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -5265,7 +5265,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.icon_filename = "cog.svg";
     item.tooltip = _utf8(L("Switch to Print Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "2]";
     item.sprite_id = 1;
-    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(0); };
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*0*/1); };
     item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed(); };
 
     if (!m_collapse_toolbar.add_item(item))
@@ -5275,7 +5275,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.icon_filename = "spool.svg";
     item.tooltip = _utf8(L("Switch to Filament Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
     item.sprite_id = 2;
-    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(1); };
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); };
     item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed() &&
                                                   wxGetApp().plater()->printer_technology() == ptFFF; };
 
@@ -5286,7 +5286,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.icon_filename = "printer.svg";
     item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]";
     item.sprite_id = 3;
-    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(2); };
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*2*/3); };
 
     if (!m_collapse_toolbar.add_item(item))
         return false;
@@ -5295,7 +5295,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.icon_filename = "resin.svg";
     item.tooltip = _utf8(L("Switch to SLA Material Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
     item.sprite_id = 4;
-    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(1); };
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); };
     item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed() &&
                                                   (m_process->current_printer_technology() == ptSLA); };
 
@@ -5306,7 +5306,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.icon_filename = "sla_printer.svg";
     item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]";
     item.sprite_id = 5;
-    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(2); };
+    item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*2*/3); };
 
     if (!m_collapse_toolbar.add_item(item))
         return false;

From 033548a56873ef5d827e0fdfa3826b4827559b55 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Sat, 25 Apr 2020 08:15:04 +0200
Subject: [PATCH 39/55] Introduction of Monotonous infill type. Fill no-sort
 only for monotonous and ironing infills.

---
 src/libslic3r/Fill/Fill.cpp             |  26 +-
 src/libslic3r/Fill/FillBase.cpp         |   2 +-
 src/libslic3r/Fill/FillBase.hpp         |   3 +
 src/libslic3r/Fill/FillRectilinear2.cpp | 401 ++++++++++++++----------
 src/libslic3r/Fill/FillRectilinear2.hpp |  19 +-
 src/libslic3r/PrintConfig.cpp           |   4 +-
 src/libslic3r/PrintConfig.hpp           |   3 +-
 src/libslic3r/PrintObject.cpp           |   1 +
 src/libslic3r/ShortestPath.cpp          |   2 +-
 src/libslic3r/SupportMaterial.cpp       |  16 +-
 10 files changed, 286 insertions(+), 191 deletions(-)

diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index f62b3ab25..ad1830e5f 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -373,7 +373,11 @@ void Layer::make_fills()
 			// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
 			f->spacing = surface_fill.params.spacing;
 			surface_fill.surface.expolygon = std::move(expoly);
-			Polylines polylines = f->fill_surface(&surface_fill.surface, params);
+			Polylines polylines;
+			try {
+				polylines = f->fill_surface(&surface_fill.surface, params);
+			} catch (InfillFailedException &) {
+			}
 	        if (! polylines.empty()) {
 		        // calculate actual flow from spacing (which might have been adjusted by the infill
 		        // pattern generator)
@@ -496,10 +500,11 @@ void Layer::make_ironing()
 		if (! layerm->slices.empty()) {
 			IroningParams ironing_params;
 			const PrintRegionConfig &config = layerm->region()->config();
-			if (config.ironing_type == IroningType::AllSolid ||
-				(config.top_solid_layers > 0 && 
-					(config.ironing_type == IroningType::TopSurfaces ||
-					 (config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr)))) {
+			if (config.ironing && 
+				(config.ironing_type == IroningType::AllSolid ||
+				 	(config.top_solid_layers > 0 && 
+						(config.ironing_type == IroningType::TopSurfaces ||
+					 	(config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr))))) {
 				if (config.perimeter_extruder == config.solid_infill_extruder || config.perimeters == 0) {
 					// Iron the whole face.
 					ironing_params.extruder = config.solid_infill_extruder;
@@ -528,6 +533,7 @@ void Layer::make_ironing()
     fill.overlap 			 = 0;
     fill_params.density 	 = 1.;
     fill_params.dont_connect = true;
+    fill_params.monotonous   = true;
 
 	for (size_t i = 0; i < by_extruder.size(); ++ i) {
 		// Find span of regions equivalent to the ironing operation.
@@ -562,13 +568,17 @@ void Layer::make_ironing()
         Surface surface_fill(stTop, ExPolygon());
         for (ExPolygon &expoly : ironing_areas) {
 			surface_fill.expolygon = std::move(expoly);
-			Polylines polylines = fill.fill_surface(&surface_fill, fill_params);
+			Polylines polylines;
+			try {
+				polylines = fill.fill_surface(&surface_fill, fill_params);
+			} catch (InfillFailedException &) {
+			}
 	        if (! polylines.empty()) {
 		        // Save into layer.
 				ExtrusionEntityCollection *eec = nullptr;
 		        ironing_params.layerm->fills.entities.push_back(eec = new ExtrusionEntityCollection());
-		        //FIXME we may not want to sort a monotonous infill.
-		        eec->no_sort = false;
+		        // Don't sort the ironing infill lines as they are monotonously ordered.
+				eec->no_sort = true;
 		        extrusion_entities_append_paths(
 		            eec->entities, std::move(polylines),
 		            erIroning,
diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp
index 3b200769c..c760218c0 100644
--- a/src/libslic3r/Fill/FillBase.cpp
+++ b/src/libslic3r/Fill/FillBase.cpp
@@ -27,7 +27,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
     case ip3DHoneycomb:         return new Fill3DHoneycomb();
     case ipGyroid:              return new FillGyroid();
     case ipRectilinear:         return new FillRectilinear2();
-//  case ipRectilinear:         return new FillRectilinear();
+    case ipMonotonous:          return new FillMonotonous();
     case ipLine:                return new FillLine();
     case ipGrid:                return new FillGrid2();
     case ipTriangles:           return new FillTriangles();
diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp
index 9fb37d2c0..083cb86ce 100644
--- a/src/libslic3r/Fill/FillBase.hpp
+++ b/src/libslic3r/Fill/FillBase.hpp
@@ -37,6 +37,9 @@ struct FillParams
     // Don't adjust spacing to fill the space evenly.
     bool        dont_adjust 	{ true };
 
+    // Monotonous infill - strictly left to right for better surface quality of top infills.
+    bool 		monotonous		{ false };
+
     // For Honeycomb.
     // we were requested to complete each loop;
     // in this case we don't try to make more continuous paths
diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp
index 3d5710d29..6dd6bb883 100644
--- a/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -951,110 +951,111 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
 	    const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr;
 
         for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) {
-		    SegmentIntersection &itsct = il.intersections[i_intersection];
-	        const Polygon 		&poly  = poly_with_offset.contour(itsct.iContour);
+		    SegmentIntersection &itsct   = il.intersections[i_intersection];
+	        const Polygon 		&poly    = poly_with_offset.contour(itsct.iContour);
+            const bool           forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour);
 
 	        // 1) Find possible connection points on the previous / next vertical line.
 		    // Find an intersection point on il_prev, intersecting i_intersection
 		    // at the same orientation as i_intersection, and being closest to i_intersection
 		    // in the number of contour segments, when following the direction of the contour.
             //FIXME this has O(n) time complexity. Likely an O(log(n)) scheme is possible.
-		    int iprev = -1;
+		    int iprev  = -1;
+            int d_prev = std::numeric_limits<int>::max();
 		    if (il_prev) {
-			    int dmin = std::numeric_limits<int>::max();
 			    for (int i = 0; i < il_prev->intersections.size(); ++ i) {
 			        const SegmentIntersection &itsct2 = il_prev->intersections[i];
 			        if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
 			            // The intersection points lie on the same contour and have the same orientation.
 			            // Find the intersection point with a shortest path in the direction of the contour.
-			            int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, false);
-			            if (d < dmin) {
+			            int d = distance_of_segmens(poly, itsct2.iSegment, itsct.iSegment, forward);
+			            if (d < d_prev) {
 			                iprev = i;
-			                dmin  = d;
+                            d_prev = d;
 			            }
 			        }
 			    }
 			}
+
             // The same for il_next.
-		    int inext = -1;
-		    if (il_next) {
-			    int dmin = std::numeric_limits<int>::max();
+		    int inext  = -1;
+            int d_next = std::numeric_limits<int>::max();
+            if (il_next) {
 			    for (int i = 0; i < il_next->intersections.size(); ++ i) {
 			        const SegmentIntersection &itsct2 = il_next->intersections[i];
 			        if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
 			            // The intersection points lie on the same contour and have the same orientation.
 			            // Find the intersection point with a shortest path in the direction of the contour.
-			            int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, true);
-			            if (d < dmin) {
+			            int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, forward);
+			            if (d < d_next) {
 			                inext = i;
-			                dmin  = d;
+                            d_next = d;
 			            }
 			        }
 			    }
 			}
 
 	        // 2) Find possible connection points on the same vertical line.
-	        int iabove = -1;
+            bool same_prev = false;
+            bool same_next = false;
             // Does the perimeter intersect the current vertical line above intrsctn?
-            for (int i = i_intersection + 1; i < il.intersections.size(); ++ i)
-                if (il.intersections[i].iContour == itsct.iContour) {
-                    iabove = i;
-                    break;
-                }
-            // Does the perimeter intersect the current vertical line below intrsctn?
-	        int ibelow = -1;
-            for (int i = i_intersection - 1; i >= 0; -- i)
-                if (il.intersections[i].iContour == itsct.iContour) {
-                    ibelow = i;
-                    break;
+            for (int i = 0; i < il.intersections.size(); ++ i)
+                if (const SegmentIntersection &it2 = il.intersections[i];
+                    i != i_intersection && it2.iContour == itsct.iContour && it2.type != itsct.type) {
+                    int d = distance_of_segmens(poly, it2.iSegment, itsct.iSegment, forward);
+                    if (d < d_prev) {
+                        iprev     = i;
+                        d_prev    = d;
+                        same_prev = true;
+                    }
+                    d = distance_of_segmens(poly, itsct.iSegment, it2.iSegment, forward);
+                    if (d < d_next) {
+                        inext     = i;
+                        d_next    = d;
+                        same_next = true;
+                    }
                 }
+            assert(iprev >= 0);
+            assert(inext >= 0);
 
-	        // 3) Sort the intersection points, clear iprev / inext / iSegBelow / iSegAbove,
-	        // if it is preceded by any other intersection point along the contour.
-	        // The perimeter contour orientation.
-	        const bool forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour);
-	        {
-	            int d_horiz = (iprev  == -1) ? std::numeric_limits<int>::max() :
-	                distance_of_segmens(poly, il_prev->intersections[iprev].iSegment, itsct.iSegment, forward);
-	            int d_down  = (ibelow == -1) ? std::numeric_limits<int>::max() :
-	                distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward);
-	            int d_up    = (iabove == -1) ? std::numeric_limits<int>::max() :
-	                distance_of_segmens(poly, il.intersections[iabove].iSegment, itsct.iSegment, forward);
-	            if (d_horiz < std::min(d_down, d_up)) {
-                    itsct.prev_on_contour 	    = iprev;
-                    itsct.prev_on_contour_type  = SegmentIntersection::LinkType::Horizontal;
-	            } else if (d_down < d_up) {
-                    itsct.prev_on_contour 		= ibelow;
-                    itsct.prev_on_contour_type  = SegmentIntersection::LinkType::Down;
-                } else {
-                    itsct.prev_on_contour       = iabove;
-                    itsct.prev_on_contour_type  = SegmentIntersection::LinkType::Up;
-                }
-	            // There should always be a link to the next intersection point on the same contour.
-	            assert(itsct.prev_on_contour != -1);
-	        }
-	        {
-	            int d_horiz = (inext  == -1) ? std::numeric_limits<int>::max() :
-	                distance_of_segmens(poly, itsct.iSegment, il_next->intersections[inext].iSegment, forward);
-	            int d_down  = (ibelow == -1) ? std::numeric_limits<int>::max() :
-	                distance_of_segmens(poly, itsct.iSegment, il.intersections[ibelow].iSegment, forward);
-	            int d_up    = (iabove == -1) ? std::numeric_limits<int>::max() :
-	                distance_of_segmens(poly, itsct.iSegment, il.intersections[iabove].iSegment, forward);
-	            if (d_horiz < std::min(d_down, d_up)) {
-                    itsct.next_on_contour 	    = inext;
-                    itsct.next_on_contour_type  = SegmentIntersection::LinkType::Horizontal;
-                } else if (d_down < d_up) {
-                    itsct.next_on_contour       = ibelow;
-                    itsct.next_on_contour_type  = SegmentIntersection::LinkType::Down;
-                } else {
-                    itsct.next_on_contour       = iabove;
-                    itsct.next_on_contour_type  = SegmentIntersection::LinkType::Up;
-                }
-	            // There should always be a link to the next intersection point on the same contour.
-	            assert(itsct.next_on_contour != -1);
-	        }
+            itsct.prev_on_contour 	    = iprev;
+            itsct.prev_on_contour_type  = same_prev ? 
+                (iprev < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) :
+                SegmentIntersection::LinkType::Horizontal;
+            itsct.next_on_contour 	    = inext;
+            itsct.next_on_contour_type  = same_next ?
+                (inext < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) :
+                SegmentIntersection::LinkType::Horizontal;
 	    }
     }
+
+#ifndef NDEBUG
+    // Validate the connectivity.
+    for (size_t i_vline = 0; i_vline + 1 < segs.size(); ++ i_vline) {
+        const SegmentedIntersectionLine &il_left    = segs[i_vline];
+        const SegmentedIntersectionLine &il_right = segs[i_vline + 1];
+        for (const SegmentIntersection &it : il_left.intersections) {
+            if (it.has_right_horizontal()) {
+                const SegmentIntersection &it_right = il_right.intersections[it.right_horizontal()];
+                // For a right link there is a symmetric left link.
+                assert(it.iContour == it_right.iContour);
+                assert(it.type == it_right.type);
+                assert(it_right.has_left_horizontal());
+                assert(it_right.left_horizontal() == int(&it - il_left.intersections.data()));
+            }
+        }
+        for (const SegmentIntersection &it : il_right.intersections) {
+            if (it.has_left_horizontal()) {
+                const SegmentIntersection &it_left = il_left.intersections[it.left_horizontal()];
+                // For a right link there is a symmetric left link.
+                assert(it.iContour == it_left.iContour);
+                assert(it.type == it_left.type);
+                assert(it_left.has_right_horizontal());
+                assert(it_left.right_horizontal() == int(&it - il_right.intersections.data()));
+            }
+        }
+    }
+#endif /* NDEBUG */
 }
 
 // Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection.
@@ -1548,7 +1549,7 @@ struct MonotonousRegion
     // If false, then ending at the same side as starting.
     bool 		flips { false };
 
-    bool        length(bool region_flipped) const { return region_flipped ? len2 : len1; }
+    float       length(bool region_flipped) const { return region_flipped ? len2 : len1; }
     int 		left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; }
     int 		right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; }
 
@@ -1580,12 +1581,16 @@ struct MonotonousRegionLink
 class AntPathMatrix
 {
 public:
-	AntPathMatrix(const std::vector<MonotonousRegion> &regions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs) : 
+	AntPathMatrix(
+		const std::vector<MonotonousRegion> 			&regions, 
+		const ExPolygonWithOffset 						&poly_with_offset, 
+		const std::vector<SegmentedIntersectionLine> 	&segs,
+		const float 									 initial_pheromone) : 
 		m_regions(regions),
 		m_poly_with_offset(poly_with_offset),
 		m_segs(segs),
 		// From end of one region to the start of another region, both flipped or not flipped.
-		m_matrix(regions.size() * regions.size() * 4) {}
+		m_matrix(regions.size() * regions.size() * 4, AntPath{ -1., -1., initial_pheromone}) {}
 
 	AntPath& operator()(const MonotonousRegion &region_from, bool flipped_from, const MonotonousRegion &region_to, bool flipped_to)
 	{
@@ -1602,12 +1607,12 @@ public:
 				int i_right = vline_from.intersections[i_from].right_horizontal();
 				if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
 					// Measure length along the contour.
-	                path.length = measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to);
+	                path.length = unscale<float>(measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to));
 				}
 			}
 			if (path.length == -1.) {
 				// Just apply the Eucledian distance of the end points.
-			    path.length = Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm();
+			    path.length = unscale<float>(Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm());
 			}
 			path.visibility = 1. / (path.length + EPSILON);
 		}
@@ -1682,86 +1687,98 @@ static SegmentIntersection& vertical_run_top(SegmentedIntersectionLine& vline, S
     return const_cast<SegmentIntersection&>(vertical_run_top(std::as_const(vline), std::as_const(start)));
 }
 
-static SegmentIntersection* left_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
+static SegmentIntersection* overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_other, SegmentIntersection::Side side)
 {
-	SegmentIntersection *left = nullptr;
-	for (SegmentIntersection *it = &start; it <= &end; ++ it) {
-		int i = it->left_horizontal();
-		if (i != -1) {
-			left = &vline_left.intersections[i];
-			break;
-		}
-	}
-	return left == nullptr ? nullptr : &vertical_run_bottom(vline_left, *left);
+	SegmentIntersection *other = nullptr;
+    assert(start.is_inner());
+    assert(end.is_inner());
+    const SegmentIntersection *it = &start;
+    for (;;) {
+        if (it->is_inner()) {
+            int i = it->horizontal(side);
+            if (i != -1) {
+                other = &vline_other.intersections[i];
+                break;
+            }
+            if (it == &end)
+                break;
+        }
+        if (it->type != SegmentIntersection::INNER_HIGH)
+            ++ it;
+        else if ((it + 1)->type == SegmentIntersection::INNER_LOW)
+            ++ it;
+        else {
+            int up = it->vertical_up();
+            if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid)
+                break;
+            it = &vline_this.intersections[up];
+            assert(it->type == SegmentIntersection::INNER_LOW);
+        }
+    }
+	return other == nullptr ? nullptr : &vertical_run_bottom(vline_other, *other);
 }
 
-static SegmentIntersection* left_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
+static SegmentIntersection* overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_other, SegmentIntersection::Side side)
 {
-	SegmentIntersection *left = nullptr;
-	for (SegmentIntersection *it = &end; it >= &start; -- it) {
-		int i = it->left_horizontal();
-		if (i != -1) {
-			left = &vline_left.intersections[i];
-			break;
-		}
-	}
-	return left == nullptr ? nullptr : &vertical_run_top(vline_left, *left);
+    SegmentIntersection *other = nullptr;
+    assert(start.is_inner());
+    assert(end.is_inner());
+    const SegmentIntersection *it = &end;
+    for (;;) {
+        if (it->is_inner()) {
+            int i = it->horizontal(side);
+            if (i != -1) {
+                other = &vline_other.intersections[i];
+                break;
+            }
+            if (it == &start)
+                break;
+        }
+        if (it->type != SegmentIntersection::INNER_LOW)
+            -- it;
+        else if ((it - 1)->type == SegmentIntersection::INNER_HIGH)
+            -- it;
+        else {
+            int down = it->vertical_down();
+            if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid)
+                break;
+            it = &vline_this.intersections[down];
+            assert(it->type == SegmentIntersection::INNER_HIGH);
+        }
+    }
+	return other == nullptr ? nullptr : &vertical_run_top(vline_other, *other);
 }
 
-static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
+static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_left)
 {
 	std::pair<SegmentIntersection*, SegmentIntersection*> out(nullptr, nullptr);
-	out.first = left_overlap_bottom(start, end, vline_left);
+	out.first = overlap_bottom(start, end, vline_this, vline_left, SegmentIntersection::Side::Left);
 	if (out.first != nullptr)
-		out.second = left_overlap_top(start, end, vline_left);
+		out.second = overlap_top(start, end, vline_this, vline_left, SegmentIntersection::Side::Left);
+    assert((out.first == nullptr && out.second == nullptr) || out.first < out.second);
 	return out;
 }
 
-static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_left)
+static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_left)
 {
 	assert((start_end.first == nullptr) == (start_end.second == nullptr));
-	return start_end.first == nullptr ? start_end : left_overlap(*start_end.first, *start_end.second, vline_left);
+	return start_end.first == nullptr ? start_end : left_overlap(*start_end.first, *start_end.second, vline_this, vline_left);
 }
 
-static SegmentIntersection* right_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
-{
-	SegmentIntersection *right = nullptr;
-	for (SegmentIntersection *it = &start; it <= &end; ++ it) {
-		int i = it->right_horizontal();
-		if (i != -1) {
-			right = &vline_right.intersections[i];
-			break;
-		}
-	}
-	return right == nullptr ? nullptr : &vertical_run_bottom(vline_right, *right);
-}
-
-static SegmentIntersection* right_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
-{
-	SegmentIntersection *right = nullptr;
-	for (SegmentIntersection *it = &end; it >= &start; -- it) {
-		int i = it->right_horizontal();
-		if (i != -1) {
-			right = &vline_right.intersections[i];
-			break;
-		}
-	}
-	return right == nullptr ? nullptr : &vertical_run_top(vline_right, *right);
-}
-
-static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
+static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_right)
 {
 	std::pair<SegmentIntersection*, SegmentIntersection*> out(nullptr, nullptr);
-	out.first = right_overlap_bottom(start, end, vline_right);
+	out.first = overlap_bottom(start, end, vline_this, vline_right, SegmentIntersection::Side::Right);
 	if (out.first != nullptr)
-		out.second = right_overlap_top(start, end, vline_right);
-	return out;
+		out.second = overlap_top(start, end, vline_this, vline_right, SegmentIntersection::Side::Right);
+    assert((out.first == nullptr && out.second == nullptr) || out.first < out.second);
+    return out;
 }
 
-static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_right)
+static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_right)
 {
 	assert((start_end.first == nullptr) == (start_end.second == nullptr));
-	return start_end.first == nullptr ? start_end : right_overlap(*start_end.first, *start_end.second, vline_right);
+	return start_end.first == nullptr ? start_end : right_overlap(*start_end.first, *start_end.second, vline_this, vline_right);
 }
 
 static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<SegmentedIntersectionLine> &segs)
@@ -1771,9 +1788,11 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
     for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) {
         SegmentedIntersectionLine  &vline_seed = segs[i_vline_seed];
     	for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) {
-	        while (i_intersection_seed + 1 < vline_seed.intersections.size() &&
+	        while (i_intersection_seed < vline_seed.intersections.size() &&
 	        	   vline_seed.intersections[i_intersection_seed].type != SegmentIntersection::INNER_LOW)
 	        	++ i_intersection_seed;
+            if (i_intersection_seed == vline_seed.intersections.size())
+                break;
 			SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed];
             SegmentIntersection *end   = &end_of_vertical_run(vline_seed, *start);
 			if (! start->consumed_vertical_up) {
@@ -1791,7 +1810,7 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
 				while (++ i_vline < segs.size()) {
 			        SegmentedIntersectionLine  &vline_left	= segs[i_vline - 1];
 			        SegmentedIntersectionLine  &vline_right = segs[i_vline];
-					std::pair<SegmentIntersection*, SegmentIntersection*> right 	      = right_overlap(left, vline_right);
+					std::pair<SegmentIntersection*, SegmentIntersection*> right 	      = right_overlap(left, vline_left, vline_right);
                     if (right.first == nullptr)
                         // No neighbor at the right side of the current segment.
                         break;
@@ -1799,7 +1818,7 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
                     if (right_top_first != right.second)
                         // This segment overlaps with multiple segments at its right side.
                         break;
-                    std::pair<SegmentIntersection*, SegmentIntersection*> right_left  = left_overlap(right, vline_left);
+                    std::pair<SegmentIntersection*, SegmentIntersection*> right_left      = left_overlap(right, vline_right, vline_left);
 					if (left != right_left)
 						// Left & right draws don't overlap exclusively, right neighbor segment overlaps with multiple segments at its left.
 						break;
@@ -1843,7 +1862,7 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> &regions, s
 		if (region.left.vline > 0) {
 			auto &vline = segs[region.left.vline];
             auto &vline_left = segs[region.left.vline - 1];
-            auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline_left);
+            auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline, vline_left);
             if (lbegin != nullptr) {
                 for (;;) {
                     MapType key(lbegin, nullptr);
@@ -1862,7 +1881,7 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> &regions, s
 		if (region.right.vline + 1 < segs.size()) {
 			auto &vline = segs[region.right.vline];
             auto &vline_right = segs[region.right.vline + 1];
-            auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline_right);
+            auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline, vline_right);
             if (rbegin != nullptr) {
 			    for (;;) {
 				    MapType key(rbegin, nullptr);
@@ -1904,24 +1923,19 @@ void monotonous_3_opt(std::vector<MonotonousRegionLink> &path, const std::vector
 	// exchange by copying the pieces.
 }
 
+// #define SLIC3R_DEBUG_ANTS
+
+template<typename... TArgs>
+inline void print_ant(const std::string& fmt, TArgs&&... args) {
+#ifdef SLIC3R_DEBUG_ANTS
+    std::cout << Slic3r::format(fmt, std::forward<TArgs>(args)...) << std::endl;
+#endif
+}
+
 // Find a run through monotonous infill blocks using an 'Ant colony" optimization method.
 static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 	std::vector<MonotonousRegion> &regions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
 {
-	// Start point of a region (left) given the direction of the initial infill line.
-	auto region_start_point = [&segs](const MonotonousRegion &region, bool dir) {
-		const SegmentedIntersectionLine &vline  = segs[region.left.vline];
-        const SegmentIntersection      	&ipt    = vline.intersections[dir ? region.left.high : region.left.low];
-		return Vec2f(float(vline.pos), float(ipt.pos()));
-	};
-	// End point of a region (right) given the direction of the initial infill line and whether the monotonous run contains
-	// even or odd number of vertical lines.
-    auto region_end_point = [&segs](const MonotonousRegion &region, bool dir) {
-        const SegmentedIntersectionLine	&vline  = segs[region.right.vline];
-        const SegmentIntersection      	&ipt    = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high];
-		return Vec2f(float(vline.pos), float(ipt.pos()));
-	};
-
 	// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed).
 	std::vector<int32_t>			left_neighbors_unprocessed(regions.size(), 0);
 	// Queue of regions, which have their left neighbors already printed.
@@ -1945,15 +1959,13 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
         MonotonousRegion    *region;
         AntPath  	        *link;
         AntPath  	        *link_flipped;
-        float                cost;
+        float                probability;
 		bool 		         dir;
 	};
 	std::vector<NextCandidate> next_candidates;
 
-	AntPathMatrix path_matrix(regions, poly_with_offset, segs);
-
 	// How many times to repeat the ant simulation.
-	constexpr int num_runs = 10;
+	constexpr int num_rounds = 10;
 	// With how many ants each of the run will be performed?
 	constexpr int num_ants = 10;
 	// Base (initial) pheromone level.
@@ -1965,15 +1977,25 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 	// Exponents of the cost function.
 	constexpr float pheromone_alpha = 1.f; // pheromone exponent
 	constexpr float pheromone_beta  = 2.f; // attractiveness weighted towards edge length
-	// Cost of traversing a link between two monotonous regions.
-	auto path_cost = [pheromone_alpha, pheromone_beta](AntPath &path) {
+
+    AntPathMatrix path_matrix(regions, poly_with_offset, segs, pheromone_initial_deposit);
+
+    // Probability (unnormalized) of traversing a link between two monotonous regions.
+	auto path_probability = [pheromone_alpha, pheromone_beta](AntPath &path) {
 		return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
 	};
-    for (int run = 0; run < num_runs; ++ run)
+
+#ifdef SLIC3R_DEBUG_ANTS
+    static int irun = 0;
+    ++ irun;
+#endif /* SLIC3R_DEBUG_ANTS */
+
+    for (int round = 0; round < num_rounds; ++ round)
 	{
 		for (int ant = 0; ant < num_ants; ++ ant) 
 		{
 			// Find a new path following the pheromones deposited by the previous ants.
+			print_ant("Round %1% ant %2%", round, ant);
 			path.clear();
 			queue = queue_initial;
 			left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
@@ -1983,12 +2005,18 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
             *(queue.begin() + first_idx) = std::move(queue.back());
             queue.pop_back();
             assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0);
+			print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)", 
+				path.back().region->left.vline, 
+                path.back().flipped ? path.back().region->left.high : path.back().region->left.low,
+                path.back().flipped ? path.back().region->left.low  : path.back().region->left.high,
+                path.back().region->right.vline, 
+                path.back().flipped == path.back().region->flips ? path.back().region->right.high : path.back().region->right.low,
+                path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high);
 
 			while (! queue.empty() || ! path.back().region->right_neighbors.empty()) {
 				// Chain.
 				MonotonousRegion 		    &region = *path.back().region;
 				bool 			  			 dir    = path.back().flipped;
-				Vec2f                    	 end_pt	= region_end_point(region, dir);
 				// Sort by distance to pt.
                 next_candidates.clear();
 				next_candidates.reserve(region.right_neighbors.size() * 2);
@@ -2001,8 +2029,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
                         AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
                         AntPath &path2 	       = path_matrix(region,   dir, *next, true);
                         AntPath &path2_flipped = path_matrix(region, ! dir, *next, false);
-                        next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false });
-                        next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true  });
+                        next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_probability(path1), false });
+                        next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_probability(path2), true  });
 					}
 				}
                 size_t num_direct_neighbors = next_candidates.size();
@@ -2014,28 +2042,30 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
                         AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
                         AntPath &path2 	       = path_matrix(region,   dir, *next, true);
                         AntPath &path2_flipped = path_matrix(region, ! dir, *next, false);
-                        next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false });
-                        next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true  });
+                        next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_probability(path1), false });
+                        next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_probability(path2), true  });
                     }
                 }
 				float dice = float(rng()) / float(rng.max());
                 std::vector<NextCandidate>::iterator take_path;
 				if (dice < probability_take_best) {
-					// Take the lowest cost path.
-					take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; });
+					// Take the highest probability path.
+					take_path = std::max_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.probability < r.probability; });
+					print_ant("\tTaking best path at probability %1% below %2%", dice,  probability_take_best);
 				} else {
-					// Take the path based on the cost.
-                    // Calculate the total cost.
-                    float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; });
-					// Take a random path based on the cost.
-                    float cost_threshold = floor(float(rng()) * total_cost / float(rng.max()));
+					// Take the path based on the probability.
+                    // Calculate the total probability.
+                    float total_probability = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.probability; });
+					// Take a random path based on the probability.
+                    float probability_threshold = float(rng()) * total_probability / float(rng.max());
                     take_path = next_candidates.end();
                     -- take_path;
                     for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it)
-                        if (cost_threshold -= it->cost <= 0.) {
+                        if ((probability_threshold -= it->probability) <= 0.) {
                             take_path = it;
                             break;
                         }
+					print_ant("\tTaking path at probability threshold %1% of %2%", probability_threshold, total_probability);
 				}
                 // Move the other right neighbors with satisified constraints to the queue.
                 bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors;
@@ -2054,6 +2084,23 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
                 path.back().next         = take_path->link;
                 path.back().next_flipped = take_path->link_flipped;
                 path.emplace_back(MonotonousRegionLink{ next_region, next_dir });
+				print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%) length to prev %7%", 
+                    next_region->left.vline, 
+                    next_dir ? next_region->left.high : next_region->left.low,
+                    next_dir ? next_region->left.low  : next_region->left.high,
+					next_region->right.vline, 
+                    next_dir == next_region->flips ? next_region->right.high : next_region->right.low,
+                    next_dir == next_region->flips ? next_region->right.low  : next_region->right.high,
+					take_path->link->length);
+
+                print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)",
+                    path.back().region->left.vline,
+                    path.back().flipped ? path.back().region->left.high : path.back().region->left.low,
+                    path.back().flipped ? path.back().region->left.low : path.back().region->left.high,
+                    path.back().region->right.vline,
+                    path.back().flipped == path.back().region->flips ? path.back().region->right.high : path.back().region->right.low,
+                    path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high);
+
 				// Update pheromones along this link.
 				take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit;
 			}
@@ -2070,6 +2117,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
                     return l + r.region->length(r.flipped) + path_matrix(*r.region, r.flipped, *next.region, next.flipped).length;
                 });
 			// Save the shortest path.
+			print_ant("\tThis length: %1%, shortest length: %2%", path_length, best_path_length);
 			if (path_length < best_path_length) {
 				best_path_length = path_length;
 				std::swap(best_path, path);
@@ -2322,7 +2370,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
 #endif /* SLIC3R_DEBUG */
 
     //FIXME this is a hack to get the monotonous infill rolling. We likely want a smarter switch, likely based on user decison.
-    bool monotonous_infill = params.density > 0.99;
+    bool monotonous_infill = params.monotonous; // || params.density > 0.99;
     if (monotonous_infill) {
 		std::vector<MonotonousRegion> regions = generate_montonous_regions(segs);
 		connect_monotonous_regions(regions, segs);
@@ -2379,6 +2427,17 @@ Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParam
     return polylines_out;
 }
 
+Polylines FillMonotonous::fill_surface(const Surface *surface, const FillParams &params)
+{
+    FillParams params2 = params;
+    params2.monotonous = true;
+    Polylines polylines_out;
+    if (! fill_surface_by_lines(surface, params2, 0.f, 0.f, polylines_out)) {
+        printf("FillMonotonous::fill_surface() failed to fill a region.\n");
+    }
+    return polylines_out;
+}
+
 Polylines FillGrid2::fill_surface(const Surface *surface, const FillParams &params)
 {
     // Each linear fill covers half of the target coverage.
diff --git a/src/libslic3r/Fill/FillRectilinear2.hpp b/src/libslic3r/Fill/FillRectilinear2.hpp
index 4459919b0..3fe95f19c 100644
--- a/src/libslic3r/Fill/FillRectilinear2.hpp
+++ b/src/libslic3r/Fill/FillRectilinear2.hpp
@@ -13,18 +13,27 @@ class FillRectilinear2 : public Fill
 {
 public:
     virtual Fill* clone() const { return new FillRectilinear2(*this); };
-    virtual ~FillRectilinear2() {}
+    virtual ~FillRectilinear2() = default;
     virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
 
 protected:
 	bool fill_surface_by_lines(const Surface *surface, const FillParams &params, float angleBase, float pattern_shift, Polylines &polylines_out);
 };
 
+class FillMonotonous : public FillRectilinear2
+{
+public:
+    virtual Fill* clone() const { return new FillMonotonous(*this); };
+    virtual ~FillMonotonous() = default;
+    virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
+	virtual bool no_sort() const { return true; }
+};
+
 class FillGrid2 : public FillRectilinear2
 {
 public:
     virtual Fill* clone() const { return new FillGrid2(*this); };
-    virtual ~FillGrid2() {}
+    virtual ~FillGrid2() = default;
     virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
 
 protected:
@@ -36,7 +45,7 @@ class FillTriangles : public FillRectilinear2
 {
 public:
     virtual Fill* clone() const { return new FillTriangles(*this); };
-    virtual ~FillTriangles() {}
+    virtual ~FillTriangles() = default;
     virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
 
 protected:
@@ -48,7 +57,7 @@ class FillStars : public FillRectilinear2
 {
 public:
     virtual Fill* clone() const { return new FillStars(*this); };
-    virtual ~FillStars() {}
+    virtual ~FillStars() = default;
     virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
 
 protected:
@@ -60,7 +69,7 @@ class FillCubic : public FillRectilinear2
 {
 public:
     virtual Fill* clone() const { return new FillCubic(*this); };
-    virtual ~FillCubic() {}
+    virtual ~FillCubic() = default;
     virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
 
 protected:
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 259b016e3..9c7a31d3e 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -418,18 +418,20 @@ void PrintConfigDef::init_fff_params()
     def->cli = "top-fill-pattern|external-fill-pattern|solid-fill-pattern";
     def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
     def->enum_values.push_back("rectilinear");
+    def->enum_values.push_back("monotonous");
     def->enum_values.push_back("concentric");
     def->enum_values.push_back("hilbertcurve");
     def->enum_values.push_back("archimedeanchords");
     def->enum_values.push_back("octagramspiral");
     def->enum_labels.push_back(L("Rectilinear"));
+    def->enum_labels.push_back(L("Monotonous"));
     def->enum_labels.push_back(L("Concentric"));
     def->enum_labels.push_back(L("Hilbert Curve"));
     def->enum_labels.push_back(L("Archimedean Chords"));
     def->enum_labels.push_back(L("Octagram Spiral"));
     // solid_fill_pattern is an obsolete equivalent to top_fill_pattern/bottom_fill_pattern.
     def->aliases = { "solid_fill_pattern", "external_fill_pattern" };
-    def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
+    def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipMonotonous));
 
     def = this->add("bottom_fill_pattern", coEnum);
     def->label = L("Bottom fill pattern");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index a7d2d8270..b3dc3f154 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -34,7 +34,7 @@ enum PrintHostType {
 };
 
 enum InfillPattern {
-    ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
+    ipRectilinear, ipMonotonous, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
     ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount,
 };
 
@@ -113,6 +113,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::g
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
         keys_map["rectilinear"]         = ipRectilinear;
+        keys_map["monotonous"]          = ipMonotonous;
         keys_map["grid"]                = ipGrid;
         keys_map["triangles"]           = ipTriangles;
         keys_map["stars"]               = ipStars;
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index a68a9605b..5d8d6a756 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -2629,6 +2629,7 @@ void PrintObject::combine_infill()
              // Because fill areas for rectilinear and honeycomb are grown 
              // later to overlap perimeters, we need to counteract that too.
                 ((region->config().fill_pattern == ipRectilinear   ||
+                  region->config().fill_pattern == ipMonotonous    ||
                   region->config().fill_pattern == ipGrid          ||
                   region->config().fill_pattern == ipLine          ||
                   region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * 
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index 0aa897fd7..01f39872f 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -38,7 +38,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<En
     	// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
 		size_t next_idx = find_closest_point(kdtree, this_point.pos,
 			[this_idx, &end_points, &could_reverse_func](size_t idx) {
-				return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1));
+				return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx & 1) == 0 || could_reverse_func(idx >> 1));
 		});
 		assert(next_idx < end_points.size());
 		EndPointType &end_point = end_points[next_idx];
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index 1d98bb0e6..72b4f465b 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -2317,10 +2317,15 @@ static inline void fill_expolygons_generate_paths(
     fill_params.dont_adjust = true;
     for (const ExPolygon &expoly : expolygons) {
         Surface surface(stInternal, expoly);
+        Polylines polylines;
+    	try {
+            polylines = filler->fill_surface(&surface, fill_params);
+		} catch (InfillFailedException &) {
+		}
         extrusion_entities_append_paths(
             dst,
-            filler->fill_surface(&surface, fill_params),
-            role, 
+            std::move(polylines),
+            role,
             flow.mm3_per_mm(), flow.width, flow.height);
     }
 }
@@ -2339,9 +2344,14 @@ static inline void fill_expolygons_generate_paths(
     fill_params.dont_adjust = true;
     for (ExPolygon &expoly : expolygons) {
         Surface surface(stInternal, std::move(expoly));
+        Polylines polylines;
+    	try {
+            polylines = filler->fill_surface(&surface, fill_params);
+		} catch (InfillFailedException &) {
+		}
         extrusion_entities_append_paths(
             dst,
-            filler->fill_surface(&surface, fill_params),
+            std::move(polylines),
             role,
             flow.mm3_per_mm(), flow.width, flow.height);
     }

From 93170870e8898c61d3843fe6d99301ece7419e35 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 27 Apr 2020 11:51:17 +0200
Subject: [PATCH 40/55] Fixed update(clear) of the search_line, when search
 dialog was opened and closed without changing

---
 src/slic3r/GUI/GLCanvas3D.cpp  | 19 +++++++------------
 src/slic3r/GUI/GUI_Preview.cpp |  1 +
 src/slic3r/GUI/Tab.cpp         | 10 +---------
 src/slic3r/GUI/Tab.hpp         |  1 -
 4 files changed, 9 insertions(+), 22 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 95cf4921f..8220db8d7 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -4523,15 +4523,13 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
 
     search_line = s;
     delete [] s;
+    if (search_line == _u8L("Type here to search"))
+        search_line.clear();
 
-    if (edited) {
-        if (search_line == _u8L("Type here to search"))
-            search_line.clear();
+    if (edited)
         sidebar.search();
-    }
 
-    if (selected != size_t(-1))
-    {
+    if (selected != size_t(-1)) {
         // selected == 9999 means that Esc kye was pressed
         if (selected != 9999)
             sidebar.jump_to_option(selected);
@@ -5252,7 +5250,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
         m_collapse_toolbar.set_tooltip(id, new_tooltip);
         set_tooltip("");
 
-        post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR));
+        wxGetApp().plater()->collapse_sidebur(!wxGetApp().plater()->is_sidebar_collapsed());
     };
 
     if (!m_collapse_toolbar.add_item(item))
@@ -5266,7 +5264,6 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.tooltip = _utf8(L("Switch to Print Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "2]";
     item.sprite_id = 1;
     item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*0*/1); };
-    item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed(); };
 
     if (!m_collapse_toolbar.add_item(item))
         return false;
@@ -5276,8 +5273,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.tooltip = _utf8(L("Switch to Filament Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
     item.sprite_id = 2;
     item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); };
-    item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed() &&
-                                                  wxGetApp().plater()->printer_technology() == ptFFF; };
+    item.visibility_callback  = [this]() { return wxGetApp().plater()->printer_technology() == ptFFF; };
 
     if (!m_collapse_toolbar.add_item(item))
         return false;
@@ -5296,8 +5292,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
     item.tooltip = _utf8(L("Switch to SLA Material Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
     item.sprite_id = 4;
     item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); };
-    item.visibility_callback  = [this]() { return wxGetApp().plater()->is_sidebar_collapsed() &&
-                                                  (m_process->current_printer_technology() == ptSLA); };
+    item.visibility_callback  = [this]() { return m_process->current_printer_technology() == ptSLA; };
 
     if (!m_collapse_toolbar.add_item(item))
         return false;
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 47a0d3074..8713e2e21 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -271,6 +271,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
     m_canvas->set_process(m_process);
     m_canvas->enable_legend_texture(true);
     m_canvas->enable_dynamic_background(true);
+    m_canvas->enable_collapse_toolbar(true);
 
     m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL);
     create_double_slider();
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 7b054835d..1baa1f47f 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -189,9 +189,6 @@ void Tab::create_preset_tab()
     add_scaled_button(panel, &m_search_btn, "search");
     m_search_btn->SetToolTip(format_wxstr(_L("Click to start a search or use %1% shortcut"), "Ctrl+F"));
 
-    add_scaled_button(panel, &m_to_plater_btn, "plater");
-    m_to_plater_btn->SetToolTip(_L("Switch to the Plater"));
-
     // Determine the theme color of OS (dark or light)
     auto luma = wxGetApp().get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
     // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
@@ -223,7 +220,6 @@ void Tab::create_preset_tab()
         }
     }));
     m_search_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { wxGetApp().plater()->search(false); });
-    m_to_plater_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { wxGetApp().mainframe->switch_to(true); });
 
     // Colors for ui "decoration"
     m_sys_label_clr			= wxGetApp().get_label_clr_sys();
@@ -253,12 +249,8 @@ void Tab::create_preset_tab()
     // m_hsizer->AddStretchSpacer(32);
     // StretchSpacer has a strange behavior under OSX, so
     // There is used just additional sizer for m_mode_sizer with right alignment
-    wxBoxSizer* top_right_sizer = new wxBoxSizer(wxHORIZONTAL);
-    top_right_sizer->Add(m_to_plater_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, wxOSX ? 15 : 10);
-    top_right_sizer->Add(m_mode_sizer, 0, wxALIGN_CENTER_VERTICAL);
     auto mode_sizer = new wxBoxSizer(wxVERTICAL);
-//    mode_sizer->Add(m_mode_sizer, 1, wxALIGN_RIGHT);
-    mode_sizer->Add(top_right_sizer, 1, wxALIGN_RIGHT);
+    mode_sizer->Add(m_mode_sizer, 1, wxALIGN_RIGHT);
     m_hsizer->Add(mode_sizer, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, wxOSX ? 15 : 10);
 
     //Horizontal sizer to hold the tree and the selected page.
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 13aee9a5f..7b231daf8 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -126,7 +126,6 @@ protected:
 	ScalableButton*		m_btn_save_preset;
 	ScalableButton*		m_btn_delete_preset;
 	ScalableButton*		m_btn_hide_incompatible_presets;
-	ScalableButton*		m_to_plater_btn;
 	wxBoxSizer*			m_hsizer;
 	wxBoxSizer*			m_left_sizer;
 	wxTreeCtrl*			m_treectrl;

From 60ae7d67e9ef3adb006d51ff73a59be020b3e501 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Mon, 27 Apr 2020 16:00:54 +0200
Subject: [PATCH 41/55] Implemented workaround for the mouse wheel in Search
 Window and Undo/Redo lists on the Plater

Workaround is used because of ImGui::GetIO().MouseWheel returns zero always!
---
 src/slic3r/GUI/GLCanvas3D.cpp   | 13 ++++++-
 src/slic3r/GUI/GLCanvas3D.hpp   |  1 +
 src/slic3r/GUI/ImGuiWrapper.cpp | 68 ++++++++++++++++++++-------------
 src/slic3r/GUI/ImGuiWrapper.hpp |  4 +-
 4 files changed, 56 insertions(+), 30 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 8220db8d7..2f6808c04 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3458,6 +3458,15 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
         }
     }
 
+    // If the Search window or Undo/Redo list is opened, 
+    // update them according to the event
+    if (m_main_toolbar.is_item_pressed("search")    || 
+        m_undoredo_toolbar.is_item_pressed("undo")  || 
+        m_undoredo_toolbar.is_item_pressed("redo")) {
+        m_mouse_wheel = int((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
+        return;
+    }
+
     // Inform gizmos about the event so they have the opportunity to react.
     if (m_gizmos.on_mouse_wheel(evt))
         return;
@@ -4465,7 +4474,7 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const
 	em *= m_retina_helper->get_scale_factor();
 #endif
 
-    if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected))
+    if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected, m_mouse_wheel))
         m_imgui_undo_redo_hovered_pos = hovered;
     else
         m_imgui_undo_redo_hovered_pos = -1;
@@ -4519,7 +4528,7 @@ bool GLCanvas3D::_render_search_list(float pos_x) const
 
     imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, 
                        sidebar.get_searcher().view_params,
-                       selected, edited);
+                       selected, edited, m_mouse_wheel);
 
     search_line = s;
     delete [] s;
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 73ec88a13..e36a0bee5 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -506,6 +506,7 @@ private:
 #endif // ENABLE_RENDER_STATISTICS
 
     mutable int m_imgui_undo_redo_hovered_pos{ -1 };
+    mutable int m_mouse_wheel {0};
     int m_selected_extruder;
 
     Labels m_labels;
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index bbe7c088b..ea8b2afd3 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -379,7 +379,40 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>&
     return res;
 }
 
-bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected)
+// Scroll up for one item 
+static void scroll_up()
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+
+    float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
+    float win_top = window->Scroll.y;
+
+    ImGui::SetScrollY(win_top - item_size_y);
+}
+
+// Scroll down for one item 
+static void scroll_down()
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+
+    float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
+    float win_top = window->Scroll.y;
+
+    ImGui::SetScrollY(win_top + item_size_y);
+}
+
+static void process_mouse_wheel(int& mouse_wheel)
+{
+    if (mouse_wheel > 0)
+        scroll_up();
+    else if (mouse_wheel < 0)
+        scroll_down();
+    mouse_wheel = 0;
+}
+
+bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected, int& mouse_wheel)
 {
     bool is_hovered = false;
     ImGui::ListBoxHeader("", size);
@@ -401,6 +434,9 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (
         i++;
     }
 
+    if (is_hovered)
+        process_mouse_wheel(mouse_wheel);
+
     ImGui::ListBoxFooter();
     return is_hovered;
 }
@@ -563,30 +599,6 @@ static void scroll_y(int hover_id)
         ImGui::SetScrollY(win_top - item_size_y);
 }
 
-// Scroll up for one item 
-static void scroll_up()
-{
-    ImGuiContext& g = *GImGui;
-    ImGuiWindow* window = g.CurrentWindow;
-
-    float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
-    float win_top = window->Scroll.y;
-
-    ImGui::SetScrollY(win_top - item_size_y);
-}
-
-// Scroll down for one item 
-static void scroll_down()
-{
-    ImGuiContext& g = *GImGui;
-    ImGuiWindow* window = g.CurrentWindow;
-
-    float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
-    float win_top = window->Scroll.y;
-
-    ImGui::SetScrollY(win_top + item_size_y);
-}
-
 // Use this function instead of ImGui::IsKeyPressed.
 // ImGui::IsKeyPressed is related for *GImGui.IO.KeysDownDuration[user_key_index]
 // And after first key pressing IsKeyPressed() return "true" always even if key wasn't pressed
@@ -602,7 +614,7 @@ static void process_key_down(ImGuiKey imgui_key, std::function<void()> f)
 }
 
 void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
-                               Search::OptionViewParameters& view_params, int& selected, bool& edited)
+                               Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel)
 {
     // ImGui::ListBoxHeader("", size);
     {   
@@ -678,6 +690,10 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
 
     scroll_y(mouse_hovered);
 
+    // Process mouse wheel
+    if (mouse_hovered > 0)
+        process_mouse_wheel(mouse_wheel);
+
     // process Up/DownArrows and Enter
     process_key_down(ImGuiKey_UpArrow, [&hovered_id, mouse_hovered]() {
         if (mouse_hovered > 0)
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index a5195ad39..6a1e27dcb 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -77,9 +77,9 @@ public:
     bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
     bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
     bool combo(const wxString& label, const std::vector<std::string>& options, int& selection);   // Use -1 to not mark any option as selected
-    bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
+    bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel);
     void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
-                     Search::OptionViewParameters& view_params, int& selected, bool& edited);
+                     Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel);
 
     void disabled_begin(bool disabled);
     void disabled_end();

From 2bd524849ad8aeeb8fb96f29c9ef9d28dfeaa727 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Mon, 27 Apr 2020 17:43:34 +0200
Subject: [PATCH 42/55] Custom support blockers are now working

---
 src/libslic3r/PrintObject.cpp     |  7 +++----
 src/libslic3r/SupportMaterial.cpp | 23 +++++++++++++++--------
 2 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 1370b4d0f..34b17c7bf 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -2686,7 +2686,7 @@ void PrintObject::project_and_append_custom_supports(
 
         // Iterate over all triangles.
         tbb::parallel_for(
-            tbb::blocked_range<size_t>(0, custom_facets.size() - 1),
+            tbb::blocked_range<size_t>(0, custom_facets.size()),
             [&](const tbb::blocked_range<size_t>& range) {
             for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
 
@@ -2799,10 +2799,9 @@ void PrintObject::project_and_append_custom_supports(
         // Now append the collected polygons to respective layers.
         for (auto& trg : projections_of_triangles) {
             int layer_id = trg.first_layer_id;
-            if (layer_id == 0)
-                continue;
+
             for (const LightPolygon& poly : trg.polygons) {
-                expolys[layer_id-1].emplace_back(std::move(poly.pts));
+                expolys[layer_id].emplace_back(std::move(poly.pts));
                 ++layer_id;
             }
         }
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index 647c00ec2..6cde050cf 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -1101,10 +1101,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                             if (! enforcers.empty()) {
                                 // Apply the "support enforcers".
                                 //FIXME add the "enforcers" to the sparse support regions only.
-                                const ExPolygons &enforcer = enforcers[layer_id - 1];
+                                const ExPolygons &enforcer = enforcers[layer_id];
                                 if (! enforcer.empty()) {
                                     // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
-                                    Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)),
+                                    Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(std::move(enforcer))),
                                             offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
                                     if (! new_contacts.empty()) {
                                         if (diff_polygons.empty())
@@ -1115,19 +1115,26 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
                                 }
                             }
                         }
-                        // Apply the "support blockers".
-                        if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) {
-                            // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
-                            diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id]));
-                        }
+
                         if (diff_polygons.empty())
                             continue;
 
+                        // Apply the "support blockers".
+                        if (! blockers.empty() && ! blockers[layer_id].empty()) {
+                            // Expand the blocker a bit. Custom blockers produce strips
+                            // spanning just the projection between the two slices.
+                            // Subtracting them as they are may leave unwanted narrow
+                            // residues of diff_polygons that would then be supported.
+                            diff_polygons = diff(diff_polygons,
+                                offset(union_(to_polygons(std::move(blockers[layer_id]))),
+                                       1000.*SCALED_EPSILON));
+                        }
+
                         #ifdef SLIC3R_DEBUG
                         {
                             ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", 
                                 iRun, layer_id, 
-                                std::find_if(layer.regions.begin(), layer.regions.end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions.begin()), 
+                                std::find_if(layer.regions.begin(), layer.regions.end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions.begin()),
                             get_extents(diff_polygons));
                             Slic3r::ExPolygons expolys = union_ex(diff_polygons, false);
                             svg.draw(expolys);

From 572b5ba8bbf7903d4c0b67f6f441002ee1680cab Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 28 Apr 2020 09:56:54 +0200
Subject: [PATCH 43/55] Add PNG and wxWidgets 3.1.3 uniformly to all platforms
 with cmake build.

wx is built with png as a dependency, wxpng is disabled.
---
 CMakeLists.txt                    |    9 +-
 deps/CMakeLists.txt               |   35 +-
 deps/EXPAT/EXPAT.cmake            |    9 +
 deps/EXPAT/expat/CMakeLists.txt   |   71 +
 deps/EXPAT/expat/COPYING          |   21 +
 deps/EXPAT/expat/README           |  146 +
 deps/EXPAT/expat/ascii.h          |   92 +
 deps/EXPAT/expat/asciitab.h       |   36 +
 deps/EXPAT/expat/config.cmake.in  |    4 +
 deps/EXPAT/expat/expat.h          | 1048 +++++
 deps/EXPAT/expat/expat_config.h   |   33 +
 deps/EXPAT/expat/expat_external.h |  129 +
 deps/EXPAT/expat/iasciitab.h      |   37 +
 deps/EXPAT/expat/internal.h       |   95 +
 deps/EXPAT/expat/latin1tab.h      |   36 +
 deps/EXPAT/expat/nametab.h        |  150 +
 deps/EXPAT/expat/utf8tab.h        |   37 +
 deps/EXPAT/expat/xmlparse.c       | 6458 +++++++++++++++++++++++++++++
 deps/EXPAT/expat/xmlrole.c        | 1322 ++++++
 deps/EXPAT/expat/xmlrole.h        |  114 +
 deps/EXPAT/expat/xmltok.c         | 1737 ++++++++
 deps/EXPAT/expat/xmltok.h         |  322 ++
 deps/EXPAT/expat/xmltok_impl.h    |   46 +
 deps/EXPAT/expat/xmltok_impl.inc  | 1779 ++++++++
 deps/EXPAT/expat/xmltok_ns.inc    |  115 +
 deps/GLEW/GLEW.cmake              |    3 +-
 deps/OpenCSG/OpenCSG.cmake        |    4 +-
 deps/PNG/PNG.cmake                |   13 +
 deps/deps-linux.cmake             |   48 +-
 deps/deps-macos.cmake             |   26 -
 deps/deps-mingw.cmake             |   16 -
 deps/deps-unix-common.cmake       |    8 +-
 deps/deps-windows.cmake           |   61 +-
 deps/wxWidgets/wxWidgets.cmake    |   36 +
 src/CMakeLists.txt                |    4 +
 src/libslic3r/CMakeLists.txt      |    3 +-
 src/slic3r/CMakeLists.txt         |    2 +-
 37 files changed, 13948 insertions(+), 157 deletions(-)
 create mode 100644 deps/EXPAT/EXPAT.cmake
 create mode 100644 deps/EXPAT/expat/CMakeLists.txt
 create mode 100644 deps/EXPAT/expat/COPYING
 create mode 100644 deps/EXPAT/expat/README
 create mode 100644 deps/EXPAT/expat/ascii.h
 create mode 100644 deps/EXPAT/expat/asciitab.h
 create mode 100644 deps/EXPAT/expat/config.cmake.in
 create mode 100644 deps/EXPAT/expat/expat.h
 create mode 100644 deps/EXPAT/expat/expat_config.h
 create mode 100644 deps/EXPAT/expat/expat_external.h
 create mode 100644 deps/EXPAT/expat/iasciitab.h
 create mode 100644 deps/EXPAT/expat/internal.h
 create mode 100644 deps/EXPAT/expat/latin1tab.h
 create mode 100644 deps/EXPAT/expat/nametab.h
 create mode 100644 deps/EXPAT/expat/utf8tab.h
 create mode 100644 deps/EXPAT/expat/xmlparse.c
 create mode 100644 deps/EXPAT/expat/xmlrole.c
 create mode 100644 deps/EXPAT/expat/xmlrole.h
 create mode 100644 deps/EXPAT/expat/xmltok.c
 create mode 100644 deps/EXPAT/expat/xmltok.h
 create mode 100644 deps/EXPAT/expat/xmltok_impl.h
 create mode 100644 deps/EXPAT/expat/xmltok_impl.inc
 create mode 100644 deps/EXPAT/expat/xmltok_ns.inc
 create mode 100644 deps/PNG/PNG.cmake
 create mode 100644 deps/wxWidgets/wxWidgets.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 52be8e847..691e234ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -369,9 +369,9 @@ include_directories(BEFORE SYSTEM ${EIGEN3_INCLUDE_DIR})
 
 # Find expat or use bundled version
 # Always use the system libexpat on Linux.
-if (NOT SLIC3R_STATIC OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
-    find_package(EXPAT)
-endif ()
+
+find_package(EXPAT)
+
 if (NOT EXPAT_FOUND)
     add_library(expat STATIC
         ${LIBDIR}/expat/xmlparse.c
@@ -382,7 +382,8 @@ if (NOT EXPAT_FOUND)
     set(EXPAT_INCLUDE_DIRS ${LIBDIR}/expat/)
     set(EXPAT_LIBRARIES expat)
 endif ()
-include_directories(${EXPAT_INCLUDE_DIRS})
+
+find_package(PNG REQUIRED)
 
 find_package(OpenGL REQUIRED)
 
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 8702fd6d6..3251d2c62 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -34,8 +34,10 @@ endif ()
 set(DESTDIR "${CMAKE_CURRENT_BINARY_DIR}/destdir" CACHE PATH "Destination directory")
 
 option(DEP_DEBUG "Build debug variants (only applicable on Windows)" ON)
-option(DEP_WX_STABLE "Build against wxWidgets stable 3.0 as opposed to default 3.1 (Linux only)" OFF)
-option(DEP_WX_GTK3 "Build wxWidgets against GTK3" OFF)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    option(DEP_WX_GTK3 "Build wxWidgets against GTK3" OFF)
+endif()
 
 # On developer machines, it can be enabled to speed up compilation and suppress warnings coming from IGL. 
 # FIXME:
@@ -127,12 +129,28 @@ else()
     include("deps-linux.cmake")
 endif()
 
+set(ZLIB_PKG "")
+if (NOT ZLIB_FOUND) 
+    include(ZLIB/ZLIB.cmake)
+    set(ZLIB_PKG dep_ZLIB)
+endif ()
+set(PNG_PKG "")
+if (NOT PNG_FOUND) 
+    include(PNG/PNG.cmake)
+    set(PNG_PKG dep_PNG)
+endif ()
+set(EXPAT_PKG "")
+if (NOT EXPAT_FOUND) 
+    include(EXPAT/EXPAT.cmake)
+    set(EXPAT_PKG dep_EXPAT)
+endif ()
+
 include(GLEW/GLEW.cmake)
 include(OpenCSG/OpenCSG.cmake)
-
 include(GMP/GMP.cmake)
 include(MPFR/MPFR.cmake)
 include(CGAL/CGAL.cmake)
+include(wxWidgets/wxWidgets.cmake)
 
 if (MSVC)
 
@@ -141,15 +159,17 @@ if (MSVC)
         dep_boost
         dep_tbb
         dep_libcurl
-        dep_wxwidgets
+        dep_wxWidgets
         dep_gtest
         dep_cereal
         dep_nlopt
         # dep_qhull # Experimental
-        dep_ZLIB    # on Windows we still need zlib
         dep_openvdb
         dep_OpenCSG
         dep_CGAL
+        ${PNG_PKG}
+        ${ZLIB_PKG}
+        ${EXPAT_PKG}
     )
 
 else()
@@ -159,7 +179,7 @@ else()
         dep_boost
         dep_tbb
         dep_libcurl
-        dep_wxwidgets
+        dep_wxWidgets
         dep_gtest
         dep_cereal
         dep_nlopt
@@ -167,6 +187,9 @@ else()
         dep_openvdb
         dep_OpenCSG
         dep_CGAL
+        ${PNG_PKG}
+        ${ZLIB_PKG}
+        ${EXPAT_PKG}
         # dep_libigl # Not working, static build has different Eigen
     )
 
diff --git a/deps/EXPAT/EXPAT.cmake b/deps/EXPAT/EXPAT.cmake
new file mode 100644
index 000000000..ed5eb220f
--- /dev/null
+++ b/deps/EXPAT/EXPAT.cmake
@@ -0,0 +1,9 @@
+prusaslicer_add_cmake_project(EXPAT
+  # GIT_REPOSITORY https://github.com/nigels-com/glew.git
+  # GIT_TAG 3a8eff7 # 2.1.0
+  SOURCE_DIR          ${CMAKE_CURRENT_LIST_DIR}/expat
+)
+
+if (MSVC)
+    add_debug_dep(dep_EXPAT)
+endif ()
diff --git a/deps/EXPAT/expat/CMakeLists.txt b/deps/EXPAT/expat/CMakeLists.txt
new file mode 100644
index 000000000..fa54c098f
--- /dev/null
+++ b/deps/EXPAT/expat/CMakeLists.txt
@@ -0,0 +1,71 @@
+cmake_minimum_required(VERSION 3.0)
+
+project(EXPAT)
+
+if (BUILD_SHARED_LIBS AND MSVC)
+  set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+endif()
+
+add_library(expat
+    xmlparse.c
+    xmlrole.c
+    xmltok.c
+)
+
+target_include_directories(expat PRIVATE ${PROJECT_SOURCE_DIR})
+
+include(GNUInstallDirs)
+
+install( 
+    FILES
+        ${PROJECT_SOURCE_DIR}/expat.h
+        ${PROJECT_SOURCE_DIR}/expat_config.h
+        ${PROJECT_SOURCE_DIR}/expat_external.h
+    DESTINATION
+        ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+add_library(EXPAT INTERFACE)
+target_link_libraries(EXPAT INTERFACE expat)
+
+include(CMakePackageConfigHelpers)
+
+write_basic_package_version_file(
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"     
+    VERSION 1.95
+    COMPATIBILITY AnyNewerVersion
+)
+
+install(TARGETS expat EXPAT
+  EXPORT ${PROJECT_NAME}Targets
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+export(EXPORT ${PROJECT_NAME}Targets 
+       FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake" 
+       NAMESPACE ${PROJECT_NAME}:: )
+
+set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+
+install(EXPORT ${PROJECT_NAME}Targets
+  FILE
+    "${PROJECT_NAME}Targets.cmake"
+  NAMESPACE
+    ${PROJECT_NAME}::
+  DESTINATION
+    ${ConfigPackageLocation}
+)
+
+configure_file(config.cmake.in ${PROJECT_NAME}Config.cmake @ONLY)
+
+install(
+    FILES
+      "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+      "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
+    DESTINATION
+      ${ConfigPackageLocation}
+)
+
diff --git a/deps/EXPAT/expat/COPYING b/deps/EXPAT/expat/COPYING
new file mode 100644
index 000000000..092c83bae
--- /dev/null
+++ b/deps/EXPAT/expat/COPYING
@@ -0,0 +1,21 @@
+Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
+Copyright (c) 2001-2016 Expat maintainers
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/deps/EXPAT/expat/README b/deps/EXPAT/expat/README
new file mode 100644
index 000000000..40873a6f8
--- /dev/null
+++ b/deps/EXPAT/expat/README
@@ -0,0 +1,146 @@
+Expat, Release 2.2.0, stripped and modified for inclusion into Slic3r.
+Only the library sources needed for static linking were left.
+
+The original README follows:
+---------------------------------------------------------------------
+
+
+
+                        Expat, Release 2.2.0
+
+This is Expat, a C library for parsing XML, written by James Clark.
+Expat is a stream-oriented XML parser.  This means that you register
+handlers with the parser before starting the parse.  These handlers
+are called when the parser discovers the associated structures in the
+document being parsed.  A start tag is an example of the kind of
+structures for which you may register handlers.
+
+Windows users should use the expat_win32bin package, which includes
+both precompiled libraries and executables, and source code for
+developers.
+
+Expat is free software.  You may copy, distribute, and modify it under
+the terms of the License contained in the file COPYING distributed
+with this package.  This license is the same as the MIT/X Consortium
+license.
+
+Versions of Expat that have an odd minor version (the middle number in
+the release above), are development releases and should be considered
+as beta software.  Releases with even minor version numbers are
+intended to be production grade software.
+
+If you are building Expat from a check-out from the CVS repository,
+you need to run a script that generates the configure script using the
+GNU autoconf and libtool tools.  To do this, you need to have
+autoconf 2.58 or newer. Run the script like this:
+
+        ./buildconf.sh
+
+Once this has been done, follow the same instructions as for building
+from a source distribution.
+
+To build Expat from a source distribution, you first run the
+configuration shell script in the top level distribution directory:
+
+        ./configure
+
+There are many options which you may provide to configure (which you
+can discover by running configure with the --help option).  But the
+one of most interest is the one that sets the installation directory.
+By default, the configure script will set things up to install
+libexpat into /usr/local/lib, expat.h into /usr/local/include, and
+xmlwf into /usr/local/bin.  If, for example, you'd prefer to install
+into /home/me/mystuff/lib, /home/me/mystuff/include, and
+/home/me/mystuff/bin, you can tell configure about that with:
+
+        ./configure --prefix=/home/me/mystuff
+        
+Another interesting option is to enable 64-bit integer support for
+line and column numbers and the over-all byte index:
+
+        ./configure CPPFLAGS=-DXML_LARGE_SIZE
+        
+However, such a modification would be a breaking change to the ABI
+and is therefore not recommended for general use - e.g. as part of
+a Linux distribution - but rather for builds with special requirements.
+
+After running the configure script, the "make" command will build
+things and "make install" will install things into their proper
+location.  Have a look at the "Makefile" to learn about additional
+"make" options.  Note that you need to have write permission into
+the directories into which things will be installed.
+
+If you are interested in building Expat to provide document
+information in UTF-16 encoding rather than the default UTF-8, follow
+these instructions (after having run "make distclean"):
+
+        1. For UTF-16 output as unsigned short (and version/error
+           strings as char), run:
+
+               ./configure CPPFLAGS=-DXML_UNICODE
+
+           For UTF-16 output as wchar_t (incl. version/error strings),
+           run:
+
+               ./configure CFLAGS="-g -O2 -fshort-wchar" \
+                           CPPFLAGS=-DXML_UNICODE_WCHAR_T
+
+        2. Edit the MakeFile, changing:
+
+               LIBRARY = libexpat.la
+
+           to:
+
+               LIBRARY = libexpatw.la
+
+           (Note the additional "w" in the library name.)
+
+        3. Run "make buildlib" (which builds the library only).
+           Or, to save step 2, run "make buildlib LIBRARY=libexpatw.la".
+
+        4. Run "make installlib" (which installs the library only).
+           Or, if step 2 was omitted, run "make installlib LIBRARY=libexpatw.la".
+           
+Using DESTDIR or INSTALL_ROOT is enabled, with INSTALL_ROOT being the default
+value for DESTDIR, and the rest of the make file using only DESTDIR.
+It works as follows:
+   $ make install DESTDIR=/path/to/image
+overrides the in-makefile set DESTDIR, while both
+   $ INSTALL_ROOT=/path/to/image make install
+   $ make install INSTALL_ROOT=/path/to/image
+use DESTDIR=$(INSTALL_ROOT), even if DESTDIR eventually is defined in the
+environment, because variable-setting priority is
+1) commandline
+2) in-makefile
+3) environment  
+
+Note: This only applies to the Expat library itself, building UTF-16 versions
+of xmlwf and the tests is currently not supported.         
+
+Note for Solaris users:  The "ar" command is usually located in
+"/usr/ccs/bin", which is not in the default PATH.  You will need to
+add this to your path for the "make" command, and probably also switch
+to GNU make (the "make" found in /usr/ccs/bin does not seem to work
+properly -- apparently it does not understand .PHONY directives).  If
+you're using ksh or bash, use this command to build:
+
+        PATH=/usr/ccs/bin:$PATH make
+
+When using Expat with a project using autoconf for configuration, you
+can use the probing macro in conftools/expat.m4 to determine how to
+include Expat.  See the comments at the top of that file for more
+information.
+
+A reference manual is available in the file doc/reference.html in this
+distribution.
+
+The homepage for this project is http://www.libexpat.org/.  There
+are links there to connect you to the bug reports page.  If you need
+to report a bug when you don't have access to a browser, you may also
+send a bug report by email to expat-bugs@mail.libexpat.org.
+
+Discussion related to the direction of future expat development takes
+place on expat-discuss@mail.libexpat.org.  Archives of this list and
+other Expat-related lists may be found at:
+
+        http://mail.libexpat.org/mailman/listinfo/
diff --git a/deps/EXPAT/expat/ascii.h b/deps/EXPAT/expat/ascii.h
new file mode 100644
index 000000000..d10530b09
--- /dev/null
+++ b/deps/EXPAT/expat/ascii.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+#define ASCII_A 0x41
+#define ASCII_B 0x42
+#define ASCII_C 0x43
+#define ASCII_D 0x44
+#define ASCII_E 0x45
+#define ASCII_F 0x46
+#define ASCII_G 0x47
+#define ASCII_H 0x48
+#define ASCII_I 0x49
+#define ASCII_J 0x4A
+#define ASCII_K 0x4B
+#define ASCII_L 0x4C
+#define ASCII_M 0x4D
+#define ASCII_N 0x4E
+#define ASCII_O 0x4F
+#define ASCII_P 0x50
+#define ASCII_Q 0x51
+#define ASCII_R 0x52
+#define ASCII_S 0x53
+#define ASCII_T 0x54
+#define ASCII_U 0x55
+#define ASCII_V 0x56
+#define ASCII_W 0x57
+#define ASCII_X 0x58
+#define ASCII_Y 0x59
+#define ASCII_Z 0x5A
+
+#define ASCII_a 0x61
+#define ASCII_b 0x62
+#define ASCII_c 0x63
+#define ASCII_d 0x64
+#define ASCII_e 0x65
+#define ASCII_f 0x66
+#define ASCII_g 0x67
+#define ASCII_h 0x68
+#define ASCII_i 0x69
+#define ASCII_j 0x6A
+#define ASCII_k 0x6B
+#define ASCII_l 0x6C
+#define ASCII_m 0x6D
+#define ASCII_n 0x6E
+#define ASCII_o 0x6F
+#define ASCII_p 0x70
+#define ASCII_q 0x71
+#define ASCII_r 0x72
+#define ASCII_s 0x73
+#define ASCII_t 0x74
+#define ASCII_u 0x75
+#define ASCII_v 0x76
+#define ASCII_w 0x77
+#define ASCII_x 0x78
+#define ASCII_y 0x79
+#define ASCII_z 0x7A
+
+#define ASCII_0 0x30
+#define ASCII_1 0x31
+#define ASCII_2 0x32
+#define ASCII_3 0x33
+#define ASCII_4 0x34
+#define ASCII_5 0x35
+#define ASCII_6 0x36
+#define ASCII_7 0x37
+#define ASCII_8 0x38
+#define ASCII_9 0x39
+
+#define ASCII_TAB 0x09
+#define ASCII_SPACE 0x20
+#define ASCII_EXCL 0x21
+#define ASCII_QUOT 0x22
+#define ASCII_AMP 0x26
+#define ASCII_APOS 0x27
+#define ASCII_MINUS 0x2D
+#define ASCII_PERIOD 0x2E
+#define ASCII_COLON 0x3A
+#define ASCII_SEMI 0x3B
+#define ASCII_LT 0x3C
+#define ASCII_EQUALS 0x3D
+#define ASCII_GT 0x3E
+#define ASCII_LSQB 0x5B
+#define ASCII_RSQB 0x5D
+#define ASCII_UNDERSCORE 0x5F
+#define ASCII_LPAREN 0x28
+#define ASCII_RPAREN 0x29
+#define ASCII_FF 0x0C
+#define ASCII_SLASH 0x2F
+#define ASCII_HASH 0x23
+#define ASCII_PIPE 0x7C
+#define ASCII_COMMA 0x2C
diff --git a/deps/EXPAT/expat/asciitab.h b/deps/EXPAT/expat/asciitab.h
new file mode 100644
index 000000000..79a15c28c
--- /dev/null
+++ b/deps/EXPAT/expat/asciitab.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/deps/EXPAT/expat/config.cmake.in b/deps/EXPAT/expat/config.cmake.in
new file mode 100644
index 000000000..8edb5bbbd
--- /dev/null
+++ b/deps/EXPAT/expat/config.cmake.in
@@ -0,0 +1,4 @@
+include(${CMAKE_CURRENT_LIST_DIR}/EXPATTargets.cmake)
+set(EXPAT_LIBRARIES EXPAT::expat)
+set(EXPAT_INCLUDE_DIRS ${_IMPORT_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
+
diff --git a/deps/EXPAT/expat/expat.h b/deps/EXPAT/expat/expat.h
new file mode 100644
index 000000000..086e24b39
--- /dev/null
+++ b/deps/EXPAT/expat/expat.h
@@ -0,0 +1,1048 @@
+/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+#ifndef Expat_INCLUDED
+#define Expat_INCLUDED 1
+
+#ifdef __VMS
+/*      0        1         2         3      0        1         2         3
+        1234567890123456789012345678901     1234567890123456789012345678901 */
+#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler
+#define XML_SetUnparsedEntityDeclHandler    XML_SetUnparsedEntDeclHandler
+#define XML_SetStartNamespaceDeclHandler    XML_SetStartNamespcDeclHandler
+#define XML_SetExternalEntityRefHandlerArg  XML_SetExternalEntRefHandlerArg
+#endif
+
+#include <stdlib.h>
+#include "expat_external.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct XML_ParserStruct;
+typedef struct XML_ParserStruct *XML_Parser;
+
+/* Should this be defined using stdbool.h when C99 is available? */
+typedef unsigned char XML_Bool;
+#define XML_TRUE   ((XML_Bool) 1)
+#define XML_FALSE  ((XML_Bool) 0)
+
+/* The XML_Status enum gives the possible return values for several
+   API functions.  The preprocessor #defines are included so this
+   stanza can be added to code that still needs to support older
+   versions of Expat 1.95.x:
+
+   #ifndef XML_STATUS_OK
+   #define XML_STATUS_OK    1
+   #define XML_STATUS_ERROR 0
+   #endif
+
+   Otherwise, the #define hackery is quite ugly and would have been
+   dropped.
+*/
+enum XML_Status {
+  XML_STATUS_ERROR = 0,
+#define XML_STATUS_ERROR XML_STATUS_ERROR
+  XML_STATUS_OK = 1,
+#define XML_STATUS_OK XML_STATUS_OK
+  XML_STATUS_SUSPENDED = 2
+#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED
+};
+
+enum XML_Error {
+  XML_ERROR_NONE,
+  XML_ERROR_NO_MEMORY,
+  XML_ERROR_SYNTAX,
+  XML_ERROR_NO_ELEMENTS,
+  XML_ERROR_INVALID_TOKEN,
+  XML_ERROR_UNCLOSED_TOKEN,
+  XML_ERROR_PARTIAL_CHAR,
+  XML_ERROR_TAG_MISMATCH,
+  XML_ERROR_DUPLICATE_ATTRIBUTE,
+  XML_ERROR_JUNK_AFTER_DOC_ELEMENT,
+  XML_ERROR_PARAM_ENTITY_REF,
+  XML_ERROR_UNDEFINED_ENTITY,
+  XML_ERROR_RECURSIVE_ENTITY_REF,
+  XML_ERROR_ASYNC_ENTITY,
+  XML_ERROR_BAD_CHAR_REF,
+  XML_ERROR_BINARY_ENTITY_REF,
+  XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
+  XML_ERROR_MISPLACED_XML_PI,
+  XML_ERROR_UNKNOWN_ENCODING,
+  XML_ERROR_INCORRECT_ENCODING,
+  XML_ERROR_UNCLOSED_CDATA_SECTION,
+  XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+  XML_ERROR_NOT_STANDALONE,
+  XML_ERROR_UNEXPECTED_STATE,
+  XML_ERROR_ENTITY_DECLARED_IN_PE,
+  XML_ERROR_FEATURE_REQUIRES_XML_DTD,
+  XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING,
+  /* Added in 1.95.7. */
+  XML_ERROR_UNBOUND_PREFIX,
+  /* Added in 1.95.8. */
+  XML_ERROR_UNDECLARING_PREFIX,
+  XML_ERROR_INCOMPLETE_PE,
+  XML_ERROR_XML_DECL,
+  XML_ERROR_TEXT_DECL,
+  XML_ERROR_PUBLICID,
+  XML_ERROR_SUSPENDED,
+  XML_ERROR_NOT_SUSPENDED,
+  XML_ERROR_ABORTED,
+  XML_ERROR_FINISHED,
+  XML_ERROR_SUSPEND_PE,
+  /* Added in 2.0. */
+  XML_ERROR_RESERVED_PREFIX_XML,
+  XML_ERROR_RESERVED_PREFIX_XMLNS,
+  XML_ERROR_RESERVED_NAMESPACE_URI
+};
+
+enum XML_Content_Type {
+  XML_CTYPE_EMPTY = 1,
+  XML_CTYPE_ANY,
+  XML_CTYPE_MIXED,
+  XML_CTYPE_NAME,
+  XML_CTYPE_CHOICE,
+  XML_CTYPE_SEQ
+};
+
+enum XML_Content_Quant {
+  XML_CQUANT_NONE,
+  XML_CQUANT_OPT,
+  XML_CQUANT_REP,
+  XML_CQUANT_PLUS
+};
+
+/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be
+   XML_CQUANT_NONE, and the other fields will be zero or NULL.
+   If type == XML_CTYPE_MIXED, then quant will be NONE or REP and
+   numchildren will contain number of elements that may be mixed in
+   and children point to an array of XML_Content cells that will be
+   all of XML_CTYPE_NAME type with no quantification.
+
+   If type == XML_CTYPE_NAME, then the name points to the name, and
+   the numchildren field will be zero and children will be NULL. The
+   quant fields indicates any quantifiers placed on the name.
+
+   CHOICE and SEQ will have name NULL, the number of children in
+   numchildren and children will point, recursively, to an array
+   of XML_Content cells.
+
+   The EMPTY, ANY, and MIXED types will only occur at top level.
+*/
+
+typedef struct XML_cp XML_Content;
+
+struct XML_cp {
+  enum XML_Content_Type         type;
+  enum XML_Content_Quant        quant;
+  XML_Char *                    name;
+  unsigned int                  numchildren;
+  XML_Content *                 children;
+};
+
+
+/* This is called for an element declaration. See above for
+   description of the model argument. It's the caller's responsibility
+   to free model when finished with it.
+*/
+typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData,
+                                                const XML_Char *name,
+                                                XML_Content *model);
+
+XMLPARSEAPI(void)
+XML_SetElementDeclHandler(XML_Parser parser,
+                          XML_ElementDeclHandler eldecl);
+
+/* The Attlist declaration handler is called for *each* attribute. So
+   a single Attlist declaration with multiple attributes declared will
+   generate multiple calls to this handler. The "default" parameter
+   may be NULL in the case of the "#IMPLIED" or "#REQUIRED"
+   keyword. The "isrequired" parameter will be true and the default
+   value will be NULL in the case of "#REQUIRED". If "isrequired" is
+   true and default is non-NULL, then this is a "#FIXED" default.
+*/
+typedef void (XMLCALL *XML_AttlistDeclHandler) (
+                                    void            *userData,
+                                    const XML_Char  *elname,
+                                    const XML_Char  *attname,
+                                    const XML_Char  *att_type,
+                                    const XML_Char  *dflt,
+                                    int              isrequired);
+
+XMLPARSEAPI(void)
+XML_SetAttlistDeclHandler(XML_Parser parser,
+                          XML_AttlistDeclHandler attdecl);
+
+/* The XML declaration handler is called for *both* XML declarations
+   and text declarations. The way to distinguish is that the version
+   parameter will be NULL for text declarations. The encoding
+   parameter may be NULL for XML declarations. The standalone
+   parameter will be -1, 0, or 1 indicating respectively that there
+   was no standalone parameter in the declaration, that it was given
+   as no, or that it was given as yes.
+*/
+typedef void (XMLCALL *XML_XmlDeclHandler) (void           *userData,
+                                            const XML_Char *version,
+                                            const XML_Char *encoding,
+                                            int             standalone);
+
+XMLPARSEAPI(void)
+XML_SetXmlDeclHandler(XML_Parser parser,
+                      XML_XmlDeclHandler xmldecl);
+
+
+typedef struct {
+  void *(*malloc_fcn)(size_t size);
+  void *(*realloc_fcn)(void *ptr, size_t size);
+  void (*free_fcn)(void *ptr);
+} XML_Memory_Handling_Suite;
+
+/* Constructs a new parser; encoding is the encoding specified by the
+   external protocol or NULL if there is none specified.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreate(const XML_Char *encoding);
+
+/* Constructs a new parser and namespace processor.  Element type
+   names and attribute names that belong to a namespace will be
+   expanded; unprefixed attribute names are never expanded; unprefixed
+   element type names are expanded only if there is a default
+   namespace. The expanded name is the concatenation of the namespace
+   URI, the namespace separator character, and the local part of the
+   name.  If the namespace separator is '\0' then the namespace URI
+   and the local part will be concatenated without any separator.
+   It is a programming error to use the separator '\0' with namespace
+   triplets (see XML_SetReturnNSTriplet).
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator);
+
+
+/* Constructs a new parser using the memory management suite referred to
+   by memsuite. If memsuite is NULL, then use the standard library memory
+   suite. If namespaceSeparator is non-NULL it creates a parser with
+   namespace processing as described above. The character pointed at
+   will serve as the namespace separator.
+
+   All further memory operations used for the created parser will come from
+   the given suite.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ParserCreate_MM(const XML_Char *encoding,
+                    const XML_Memory_Handling_Suite *memsuite,
+                    const XML_Char *namespaceSeparator);
+
+/* Prepare a parser object to be re-used.  This is particularly
+   valuable when memory allocation overhead is disproportionatly high,
+   such as when a large number of small documnents need to be parsed.
+   All handlers are cleared from the parser, except for the
+   unknownEncodingHandler. The parser's external state is re-initialized
+   except for the values of ns and ns_triplets.
+
+   Added in Expat 1.95.3.
+*/
+XMLPARSEAPI(XML_Bool)
+XML_ParserReset(XML_Parser parser, const XML_Char *encoding);
+
+/* atts is array of name/value pairs, terminated by 0;
+   names and values are 0 terminated.
+*/
+typedef void (XMLCALL *XML_StartElementHandler) (void *userData,
+                                                 const XML_Char *name,
+                                                 const XML_Char **atts);
+
+typedef void (XMLCALL *XML_EndElementHandler) (void *userData,
+                                               const XML_Char *name);
+
+
+/* s is not 0 terminated. */
+typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData,
+                                                  const XML_Char *s,
+                                                  int len);
+
+/* target and data are 0 terminated */
+typedef void (XMLCALL *XML_ProcessingInstructionHandler) (
+                                                void *userData,
+                                                const XML_Char *target,
+                                                const XML_Char *data);
+
+/* data is 0 terminated */
+typedef void (XMLCALL *XML_CommentHandler) (void *userData,
+                                            const XML_Char *data);
+
+typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData);
+typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData);
+
+/* This is called for any characters in the XML document for which
+   there is no applicable handler.  This includes both characters that
+   are part of markup which is of a kind that is not reported
+   (comments, markup declarations), or characters that are part of a
+   construct which could be reported but for which no handler has been
+   supplied. The characters are passed exactly as they were in the XML
+   document except that they will be encoded in UTF-8 or UTF-16.
+   Line boundaries are not normalized. Note that a byte order mark
+   character is not passed to the default handler. There are no
+   guarantees about how characters are divided between calls to the
+   default handler: for example, a comment might be split between
+   multiple calls.
+*/
+typedef void (XMLCALL *XML_DefaultHandler) (void *userData,
+                                            const XML_Char *s,
+                                            int len);
+
+/* This is called for the start of the DOCTYPE declaration, before
+   any DTD or internal subset is parsed.
+*/
+typedef void (XMLCALL *XML_StartDoctypeDeclHandler) (
+                                            void *userData,
+                                            const XML_Char *doctypeName,
+                                            const XML_Char *sysid,
+                                            const XML_Char *pubid,
+                                            int has_internal_subset);
+
+/* This is called for the start of the DOCTYPE declaration when the
+   closing > is encountered, but after processing any external
+   subset.
+*/
+typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData);
+
+/* This is called for entity declarations. The is_parameter_entity
+   argument will be non-zero if the entity is a parameter entity, zero
+   otherwise.
+
+   For internal entities (<!ENTITY foo "bar">), value will
+   be non-NULL and systemId, publicID, and notationName will be NULL.
+   The value string is NOT nul-terminated; the length is provided in
+   the value_length argument. Since it is legal to have zero-length
+   values, do not use this argument to test for internal entities.
+
+   For external entities, value will be NULL and systemId will be
+   non-NULL. The publicId argument will be NULL unless a public
+   identifier was provided. The notationName argument will have a
+   non-NULL value only for unparsed entity declarations.
+
+   Note that is_parameter_entity can't be changed to XML_Bool, since
+   that would break binary compatibility.
+*/
+typedef void (XMLCALL *XML_EntityDeclHandler) (
+                              void *userData,
+                              const XML_Char *entityName,
+                              int is_parameter_entity,
+                              const XML_Char *value,
+                              int value_length,
+                              const XML_Char *base,
+                              const XML_Char *systemId,
+                              const XML_Char *publicId,
+                              const XML_Char *notationName);
+
+XMLPARSEAPI(void)
+XML_SetEntityDeclHandler(XML_Parser parser,
+                         XML_EntityDeclHandler handler);
+
+/* OBSOLETE -- OBSOLETE -- OBSOLETE
+   This handler has been superseded by the EntityDeclHandler above.
+   It is provided here for backward compatibility.
+
+   This is called for a declaration of an unparsed (NDATA) entity.
+   The base argument is whatever was set by XML_SetBase. The
+   entityName, systemId and notationName arguments will never be
+   NULL. The other arguments may be.
+*/
+typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) (
+                                    void *userData,
+                                    const XML_Char *entityName,
+                                    const XML_Char *base,
+                                    const XML_Char *systemId,
+                                    const XML_Char *publicId,
+                                    const XML_Char *notationName);
+
+/* This is called for a declaration of notation.  The base argument is
+   whatever was set by XML_SetBase. The notationName will never be
+   NULL.  The other arguments can be.
+*/
+typedef void (XMLCALL *XML_NotationDeclHandler) (
+                                    void *userData,
+                                    const XML_Char *notationName,
+                                    const XML_Char *base,
+                                    const XML_Char *systemId,
+                                    const XML_Char *publicId);
+
+/* When namespace processing is enabled, these are called once for
+   each namespace declaration. The call to the start and end element
+   handlers occur between the calls to the start and end namespace
+   declaration handlers. For an xmlns attribute, prefix will be
+   NULL.  For an xmlns="" attribute, uri will be NULL.
+*/
+typedef void (XMLCALL *XML_StartNamespaceDeclHandler) (
+                                    void *userData,
+                                    const XML_Char *prefix,
+                                    const XML_Char *uri);
+
+typedef void (XMLCALL *XML_EndNamespaceDeclHandler) (
+                                    void *userData,
+                                    const XML_Char *prefix);
+
+/* This is called if the document is not standalone, that is, it has an
+   external subset or a reference to a parameter entity, but does not
+   have standalone="yes". If this handler returns XML_STATUS_ERROR,
+   then processing will not continue, and the parser will return a
+   XML_ERROR_NOT_STANDALONE error.
+   If parameter entity parsing is enabled, then in addition to the
+   conditions above this handler will only be called if the referenced
+   entity was actually read.
+*/
+typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData);
+
+/* This is called for a reference to an external parsed general
+   entity.  The referenced entity is not automatically parsed.  The
+   application can parse it immediately or later using
+   XML_ExternalEntityParserCreate.
+
+   The parser argument is the parser parsing the entity containing the
+   reference; it can be passed as the parser argument to
+   XML_ExternalEntityParserCreate.  The systemId argument is the
+   system identifier as specified in the entity declaration; it will
+   not be NULL.
+
+   The base argument is the system identifier that should be used as
+   the base for resolving systemId if systemId was relative; this is
+   set by XML_SetBase; it may be NULL.
+
+   The publicId argument is the public identifier as specified in the
+   entity declaration, or NULL if none was specified; the whitespace
+   in the public identifier will have been normalized as required by
+   the XML spec.
+
+   The context argument specifies the parsing context in the format
+   expected by the context argument to XML_ExternalEntityParserCreate;
+   context is valid only until the handler returns, so if the
+   referenced entity is to be parsed later, it must be copied.
+   context is NULL only when the entity is a parameter entity.
+
+   The handler should return XML_STATUS_ERROR if processing should not
+   continue because of a fatal error in the handling of the external
+   entity.  In this case the calling parser will return an
+   XML_ERROR_EXTERNAL_ENTITY_HANDLING error.
+
+   Note that unlike other handlers the first argument is the parser,
+   not userData.
+*/
+typedef int (XMLCALL *XML_ExternalEntityRefHandler) (
+                                    XML_Parser parser,
+                                    const XML_Char *context,
+                                    const XML_Char *base,
+                                    const XML_Char *systemId,
+                                    const XML_Char *publicId);
+
+/* This is called in two situations:
+   1) An entity reference is encountered for which no declaration
+      has been read *and* this is not an error.
+   2) An internal entity reference is read, but not expanded, because
+      XML_SetDefaultHandler has been called.
+   Note: skipped parameter entities in declarations and skipped general
+         entities in attribute values cannot be reported, because
+         the event would be out of sync with the reporting of the
+         declarations or attribute values
+*/
+typedef void (XMLCALL *XML_SkippedEntityHandler) (
+                                    void *userData,
+                                    const XML_Char *entityName,
+                                    int is_parameter_entity);
+
+/* This structure is filled in by the XML_UnknownEncodingHandler to
+   provide information to the parser about encodings that are unknown
+   to the parser.
+
+   The map[b] member gives information about byte sequences whose
+   first byte is b.
+
+   If map[b] is c where c is >= 0, then b by itself encodes the
+   Unicode scalar value c.
+
+   If map[b] is -1, then the byte sequence is malformed.
+
+   If map[b] is -n, where n >= 2, then b is the first byte of an
+   n-byte sequence that encodes a single Unicode scalar value.
+
+   The data member will be passed as the first argument to the convert
+   function.
+
+   The convert function is used to convert multibyte sequences; s will
+   point to a n-byte sequence where map[(unsigned char)*s] == -n.  The
+   convert function must return the Unicode scalar value represented
+   by this byte sequence or -1 if the byte sequence is malformed.
+
+   The convert function may be NULL if the encoding is a single-byte
+   encoding, that is if map[b] >= -1 for all bytes b.
+
+   When the parser is finished with the encoding, then if release is
+   not NULL, it will call release passing it the data member; once
+   release has been called, the convert function will not be called
+   again.
+
+   Expat places certain restrictions on the encodings that are supported
+   using this mechanism.
+
+   1. Every ASCII character that can appear in a well-formed XML document,
+      other than the characters
+
+      $@\^`{}~
+
+      must be represented by a single byte, and that byte must be the
+      same byte that represents that character in ASCII.
+
+   2. No character may require more than 4 bytes to encode.
+
+   3. All characters encoded must have Unicode scalar values <=
+      0xFFFF, (i.e., characters that would be encoded by surrogates in
+      UTF-16 are  not allowed).  Note that this restriction doesn't
+      apply to the built-in support for UTF-8 and UTF-16.
+
+   4. No Unicode character may be encoded by more than one distinct
+      sequence of bytes.
+*/
+typedef struct {
+  int map[256];
+  void *data;
+  int (XMLCALL *convert)(void *data, const char *s);
+  void (XMLCALL *release)(void *data);
+} XML_Encoding;
+
+/* This is called for an encoding that is unknown to the parser.
+
+   The encodingHandlerData argument is that which was passed as the
+   second argument to XML_SetUnknownEncodingHandler.
+
+   The name argument gives the name of the encoding as specified in
+   the encoding declaration.
+
+   If the callback can provide information about the encoding, it must
+   fill in the XML_Encoding structure, and return XML_STATUS_OK.
+   Otherwise it must return XML_STATUS_ERROR.
+
+   If info does not describe a suitable encoding, then the parser will
+   return an XML_UNKNOWN_ENCODING error.
+*/
+typedef int (XMLCALL *XML_UnknownEncodingHandler) (
+                                    void *encodingHandlerData,
+                                    const XML_Char *name,
+                                    XML_Encoding *info);
+
+XMLPARSEAPI(void)
+XML_SetElementHandler(XML_Parser parser,
+                      XML_StartElementHandler start,
+                      XML_EndElementHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartElementHandler(XML_Parser parser,
+                           XML_StartElementHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetEndElementHandler(XML_Parser parser,
+                         XML_EndElementHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetCharacterDataHandler(XML_Parser parser,
+                            XML_CharacterDataHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+                                    XML_ProcessingInstructionHandler handler);
+XMLPARSEAPI(void)
+XML_SetCommentHandler(XML_Parser parser,
+                      XML_CommentHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetCdataSectionHandler(XML_Parser parser,
+                           XML_StartCdataSectionHandler start,
+                           XML_EndCdataSectionHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartCdataSectionHandler(XML_Parser parser,
+                                XML_StartCdataSectionHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndCdataSectionHandler(XML_Parser parser,
+                              XML_EndCdataSectionHandler end);
+
+/* This sets the default handler and also inhibits expansion of
+   internal entities. These entity references will be passed to the
+   default handler, or to the skipped entity handler, if one is set.
+*/
+XMLPARSEAPI(void)
+XML_SetDefaultHandler(XML_Parser parser,
+                      XML_DefaultHandler handler);
+
+/* This sets the default handler but does not inhibit expansion of
+   internal entities.  The entity reference will not be passed to the
+   default handler.
+*/
+XMLPARSEAPI(void)
+XML_SetDefaultHandlerExpand(XML_Parser parser,
+                            XML_DefaultHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetDoctypeDeclHandler(XML_Parser parser,
+                          XML_StartDoctypeDeclHandler start,
+                          XML_EndDoctypeDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartDoctypeDeclHandler(XML_Parser parser,
+                               XML_StartDoctypeDeclHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndDoctypeDeclHandler(XML_Parser parser,
+                             XML_EndDoctypeDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+                                 XML_UnparsedEntityDeclHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetNotationDeclHandler(XML_Parser parser,
+                           XML_NotationDeclHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+                            XML_StartNamespaceDeclHandler start,
+                            XML_EndNamespaceDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetStartNamespaceDeclHandler(XML_Parser parser,
+                                 XML_StartNamespaceDeclHandler start);
+
+XMLPARSEAPI(void)
+XML_SetEndNamespaceDeclHandler(XML_Parser parser,
+                               XML_EndNamespaceDeclHandler end);
+
+XMLPARSEAPI(void)
+XML_SetNotStandaloneHandler(XML_Parser parser,
+                            XML_NotStandaloneHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+                                XML_ExternalEntityRefHandler handler);
+
+/* If a non-NULL value for arg is specified here, then it will be
+   passed as the first argument to the external entity ref handler
+   instead of the parser object.
+*/
+XMLPARSEAPI(void)
+XML_SetExternalEntityRefHandlerArg(XML_Parser parser,
+                                   void *arg);
+
+XMLPARSEAPI(void)
+XML_SetSkippedEntityHandler(XML_Parser parser,
+                            XML_SkippedEntityHandler handler);
+
+XMLPARSEAPI(void)
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+                              XML_UnknownEncodingHandler handler,
+                              void *encodingHandlerData);
+
+/* This can be called within a handler for a start element, end
+   element, processing instruction or character data.  It causes the
+   corresponding markup to be passed to the default handler.
+*/
+XMLPARSEAPI(void)
+XML_DefaultCurrent(XML_Parser parser);
+
+/* If do_nst is non-zero, and namespace processing is in effect, and
+   a name has a prefix (i.e. an explicit namespace qualifier) then
+   that name is returned as a triplet in a single string separated by
+   the separator character specified when the parser was created: URI
+   + sep + local_name + sep + prefix.
+
+   If do_nst is zero, then namespace information is returned in the
+   default manner (URI + sep + local_name) whether or not the name
+   has a prefix.
+
+   Note: Calling XML_SetReturnNSTriplet after XML_Parse or
+     XML_ParseBuffer has no effect.
+*/
+
+XMLPARSEAPI(void)
+XML_SetReturnNSTriplet(XML_Parser parser, int do_nst);
+
+/* This value is passed as the userData argument to callbacks. */
+XMLPARSEAPI(void)
+XML_SetUserData(XML_Parser parser, void *userData);
+
+/* Returns the last value set by XML_SetUserData or NULL. */
+#define XML_GetUserData(parser) (*(void **)(parser))
+
+/* This is equivalent to supplying an encoding argument to
+   XML_ParserCreate. On success XML_SetEncoding returns non-zero,
+   zero otherwise.
+   Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer
+     has no effect and returns XML_STATUS_ERROR.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_SetEncoding(XML_Parser parser, const XML_Char *encoding);
+
+/* If this function is called, then the parser will be passed as the
+   first argument to callbacks instead of userData.  The userData will
+   still be accessible using XML_GetUserData.
+*/
+XMLPARSEAPI(void)
+XML_UseParserAsHandlerArg(XML_Parser parser);
+
+/* If useDTD == XML_TRUE is passed to this function, then the parser
+   will assume that there is an external subset, even if none is
+   specified in the document. In such a case the parser will call the
+   externalEntityRefHandler with a value of NULL for the systemId
+   argument (the publicId and context arguments will be NULL as well).
+   Note: For the purpose of checking WFC: Entity Declared, passing
+     useDTD == XML_TRUE will make the parser behave as if the document
+     had a DTD with an external subset.
+   Note: If this function is called, then this must be done before
+     the first call to XML_Parse or XML_ParseBuffer, since it will
+     have no effect after that.  Returns
+     XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING.
+   Note: If the document does not have a DOCTYPE declaration at all,
+     then startDoctypeDeclHandler and endDoctypeDeclHandler will not
+     be called, despite an external subset being parsed.
+   Note: If XML_DTD is not defined when Expat is compiled, returns
+     XML_ERROR_FEATURE_REQUIRES_XML_DTD.
+*/
+XMLPARSEAPI(enum XML_Error)
+XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD);
+
+
+/* Sets the base to be used for resolving relative URIs in system
+   identifiers in declarations.  Resolving relative identifiers is
+   left to the application: this value will be passed through as the
+   base argument to the XML_ExternalEntityRefHandler,
+   XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base
+   argument will be copied.  Returns XML_STATUS_ERROR if out of memory,
+   XML_STATUS_OK otherwise.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_SetBase(XML_Parser parser, const XML_Char *base);
+
+XMLPARSEAPI(const XML_Char *)
+XML_GetBase(XML_Parser parser);
+
+/* Returns the number of the attribute/value pairs passed in last call
+   to the XML_StartElementHandler that were specified in the start-tag
+   rather than defaulted. Each attribute/value pair counts as 2; thus
+   this correspondds to an index into the atts array passed to the
+   XML_StartElementHandler.
+*/
+XMLPARSEAPI(int)
+XML_GetSpecifiedAttributeCount(XML_Parser parser);
+
+/* Returns the index of the ID attribute passed in the last call to
+   XML_StartElementHandler, or -1 if there is no ID attribute.  Each
+   attribute/value pair counts as 2; thus this correspondds to an
+   index into the atts array passed to the XML_StartElementHandler.
+*/
+XMLPARSEAPI(int)
+XML_GetIdAttributeIndex(XML_Parser parser);
+
+#ifdef XML_ATTR_INFO
+/* Source file byte offsets for the start and end of attribute names and values.
+   The value indices are exclusive of surrounding quotes; thus in a UTF-8 source
+   file an attribute value of "blah" will yield:
+   info->valueEnd - info->valueStart = 4 bytes.
+*/
+typedef struct {
+  XML_Index  nameStart;  /* Offset to beginning of the attribute name. */
+  XML_Index  nameEnd;    /* Offset after the attribute name's last byte. */
+  XML_Index  valueStart; /* Offset to beginning of the attribute value. */
+  XML_Index  valueEnd;   /* Offset after the attribute value's last byte. */
+} XML_AttrInfo;
+
+/* Returns an array of XML_AttrInfo structures for the attribute/value pairs
+   passed in last call to the XML_StartElementHandler that were specified
+   in the start-tag rather than defaulted. Each attribute/value pair counts
+   as 1; thus the number of entries in the array is
+   XML_GetSpecifiedAttributeCount(parser) / 2.
+*/
+XMLPARSEAPI(const XML_AttrInfo *)
+XML_GetAttributeInfo(XML_Parser parser);
+#endif
+
+/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is
+   detected.  The last call to XML_Parse must have isFinal true; len
+   may be zero for this call (or any other).
+
+   Though the return values for these functions has always been
+   described as a Boolean value, the implementation, at least for the
+   1.95.x series, has always returned exactly one of the XML_Status
+   values.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal);
+
+XMLPARSEAPI(void *)
+XML_GetBuffer(XML_Parser parser, int len);
+
+XMLPARSEAPI(enum XML_Status)
+XML_ParseBuffer(XML_Parser parser, int len, int isFinal);
+
+/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return.
+   Must be called from within a call-back handler, except when aborting
+   (resumable = 0) an already suspended parser. Some call-backs may
+   still follow because they would otherwise get lost. Examples:
+   - endElementHandler() for empty elements when stopped in
+     startElementHandler(), 
+   - endNameSpaceDeclHandler() when stopped in endElementHandler(), 
+   and possibly others.
+
+   Can be called from most handlers, including DTD related call-backs,
+   except when parsing an external parameter entity and resumable != 0.
+   Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise.
+   Possible error codes: 
+   - XML_ERROR_SUSPENDED: when suspending an already suspended parser.
+   - XML_ERROR_FINISHED: when the parser has already finished.
+   - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE.
+
+   When resumable != 0 (true) then parsing is suspended, that is, 
+   XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. 
+   Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer()
+   return XML_STATUS_ERROR with error code XML_ERROR_ABORTED.
+
+   *Note*:
+   This will be applied to the current parser instance only, that is, if
+   there is a parent parser then it will continue parsing when the
+   externalEntityRefHandler() returns. It is up to the implementation of
+   the externalEntityRefHandler() to call XML_StopParser() on the parent
+   parser (recursively), if one wants to stop parsing altogether.
+
+   When suspended, parsing can be resumed by calling XML_ResumeParser(). 
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_StopParser(XML_Parser parser, XML_Bool resumable);
+
+/* Resumes parsing after it has been suspended with XML_StopParser().
+   Must not be called from within a handler call-back. Returns same
+   status codes as XML_Parse() or XML_ParseBuffer().
+   Additional error code XML_ERROR_NOT_SUSPENDED possible.   
+
+   *Note*:
+   This must be called on the most deeply nested child parser instance
+   first, and on its parent parser only after the child parser has finished,
+   to be applied recursively until the document entity's parser is restarted.
+   That is, the parent parser will not resume by itself and it is up to the
+   application to call XML_ResumeParser() on it at the appropriate moment.
+*/
+XMLPARSEAPI(enum XML_Status)
+XML_ResumeParser(XML_Parser parser);
+
+enum XML_Parsing {
+  XML_INITIALIZED,
+  XML_PARSING,
+  XML_FINISHED,
+  XML_SUSPENDED
+};
+
+typedef struct {
+  enum XML_Parsing parsing;
+  XML_Bool finalBuffer;
+} XML_ParsingStatus;
+
+/* Returns status of parser with respect to being initialized, parsing,
+   finished, or suspended and processing the final buffer.
+   XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus,
+   XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED
+*/
+XMLPARSEAPI(void)
+XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status);
+
+/* Creates an XML_Parser object that can parse an external general
+   entity; context is a '\0'-terminated string specifying the parse
+   context; encoding is a '\0'-terminated string giving the name of
+   the externally specified encoding, or NULL if there is no
+   externally specified encoding.  The context string consists of a
+   sequence of tokens separated by formfeeds (\f); a token consisting
+   of a name specifies that the general entity of the name is open; a
+   token of the form prefix=uri specifies the namespace for a
+   particular prefix; a token of the form =uri specifies the default
+   namespace.  This can be called at any point after the first call to
+   an ExternalEntityRefHandler so longer as the parser has not yet
+   been freed.  The new parser is completely independent and may
+   safely be used in a separate thread.  The handlers and userData are
+   initialized from the parser argument.  Returns NULL if out of memory.
+   Otherwise returns a new XML_Parser object.
+*/
+XMLPARSEAPI(XML_Parser)
+XML_ExternalEntityParserCreate(XML_Parser parser,
+                               const XML_Char *context,
+                               const XML_Char *encoding);
+
+enum XML_ParamEntityParsing {
+  XML_PARAM_ENTITY_PARSING_NEVER,
+  XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE,
+  XML_PARAM_ENTITY_PARSING_ALWAYS
+};
+
+/* Controls parsing of parameter entities (including the external DTD
+   subset). If parsing of parameter entities is enabled, then
+   references to external parameter entities (including the external
+   DTD subset) will be passed to the handler set with
+   XML_SetExternalEntityRefHandler.  The context passed will be 0.
+
+   Unlike external general entities, external parameter entities can
+   only be parsed synchronously.  If the external parameter entity is
+   to be parsed, it must be parsed during the call to the external
+   entity ref handler: the complete sequence of
+   XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and
+   XML_ParserFree calls must be made during this call.  After
+   XML_ExternalEntityParserCreate has been called to create the parser
+   for the external parameter entity (context must be 0 for this
+   call), it is illegal to make any calls on the old parser until
+   XML_ParserFree has been called on the newly created parser.
+   If the library has been compiled without support for parameter
+   entity parsing (ie without XML_DTD being defined), then
+   XML_SetParamEntityParsing will return 0 if parsing of parameter
+   entities is requested; otherwise it will return non-zero.
+   Note: If XML_SetParamEntityParsing is called after XML_Parse or
+      XML_ParseBuffer, then it has no effect and will always return 0.
+*/
+XMLPARSEAPI(int)
+XML_SetParamEntityParsing(XML_Parser parser,
+                          enum XML_ParamEntityParsing parsing);
+
+/* Sets the hash salt to use for internal hash calculations.
+   Helps in preventing DoS attacks based on predicting hash
+   function behavior. This must be called before parsing is started.
+   Returns 1 if successful, 0 when called after parsing has started.
+*/
+XMLPARSEAPI(int)
+XML_SetHashSalt(XML_Parser parser,
+                unsigned long hash_salt);
+
+/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then
+   XML_GetErrorCode returns information about the error.
+*/
+XMLPARSEAPI(enum XML_Error)
+XML_GetErrorCode(XML_Parser parser);
+
+/* These functions return information about the current parse
+   location.  They may be called from any callback called to report
+   some parse event; in this case the location is the location of the
+   first of the sequence of characters that generated the event.  When
+   called from callbacks generated by declarations in the document
+   prologue, the location identified isn't as neatly defined, but will
+   be within the relevant markup.  When called outside of the callback
+   functions, the position indicated will be just past the last parse
+   event (regardless of whether there was an associated callback).
+   
+   They may also be called after returning from a call to XML_Parse
+   or XML_ParseBuffer.  If the return value is XML_STATUS_ERROR then
+   the location is the location of the character at which the error
+   was detected; otherwise the location is the location of the last
+   parse event, as described above.
+*/
+XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser);
+XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser);
+XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser);
+
+/* Return the number of bytes in the current event.
+   Returns 0 if the event is in an internal entity.
+*/
+XMLPARSEAPI(int)
+XML_GetCurrentByteCount(XML_Parser parser);
+
+/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets
+   the integer pointed to by offset to the offset within this buffer
+   of the current parse position, and sets the integer pointed to by size
+   to the size of this buffer (the number of input bytes). Otherwise
+   returns a NULL pointer. Also returns a NULL pointer if a parse isn't
+   active.
+
+   NOTE: The character pointer returned should not be used outside
+   the handler that makes the call.
+*/
+XMLPARSEAPI(const char *)
+XML_GetInputContext(XML_Parser parser,
+                    int *offset,
+                    int *size);
+
+/* For backwards compatibility with previous versions. */
+#define XML_GetErrorLineNumber   XML_GetCurrentLineNumber
+#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber
+#define XML_GetErrorByteIndex    XML_GetCurrentByteIndex
+
+/* Frees the content model passed to the element declaration handler */
+XMLPARSEAPI(void)
+XML_FreeContentModel(XML_Parser parser, XML_Content *model);
+
+/* Exposing the memory handling functions used in Expat */
+XMLPARSEAPI(void *)
+XML_ATTR_MALLOC
+XML_ATTR_ALLOC_SIZE(2)
+XML_MemMalloc(XML_Parser parser, size_t size);
+
+XMLPARSEAPI(void *)
+XML_ATTR_ALLOC_SIZE(3)
+XML_MemRealloc(XML_Parser parser, void *ptr, size_t size);
+
+XMLPARSEAPI(void)
+XML_MemFree(XML_Parser parser, void *ptr);
+
+/* Frees memory used by the parser. */
+XMLPARSEAPI(void)
+XML_ParserFree(XML_Parser parser);
+
+/* Returns a string describing the error. */
+XMLPARSEAPI(const XML_LChar *)
+XML_ErrorString(enum XML_Error code);
+
+/* Return a string containing the version number of this expat */
+XMLPARSEAPI(const XML_LChar *)
+XML_ExpatVersion(void);
+
+typedef struct {
+  int major;
+  int minor;
+  int micro;
+} XML_Expat_Version;
+
+/* Return an XML_Expat_Version structure containing numeric version
+   number information for this version of expat.
+*/
+XMLPARSEAPI(XML_Expat_Version)
+XML_ExpatVersionInfo(void);
+
+/* Added in Expat 1.95.5. */
+enum XML_FeatureEnum {
+  XML_FEATURE_END = 0,
+  XML_FEATURE_UNICODE,
+  XML_FEATURE_UNICODE_WCHAR_T,
+  XML_FEATURE_DTD,
+  XML_FEATURE_CONTEXT_BYTES,
+  XML_FEATURE_MIN_SIZE,
+  XML_FEATURE_SIZEOF_XML_CHAR,
+  XML_FEATURE_SIZEOF_XML_LCHAR,
+  XML_FEATURE_NS,
+  XML_FEATURE_LARGE_SIZE,
+  XML_FEATURE_ATTR_INFO
+  /* Additional features must be added to the end of this enum. */
+};
+
+typedef struct {
+  enum XML_FeatureEnum  feature;
+  const XML_LChar       *name;
+  long int              value;
+} XML_Feature;
+
+XMLPARSEAPI(const XML_Feature *)
+XML_GetFeatureList(void);
+
+
+/* Expat follows the semantic versioning convention.
+   See http://semver.org.
+*/
+#define XML_MAJOR_VERSION 2
+#define XML_MINOR_VERSION 2
+#define XML_MICRO_VERSION 0
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not Expat_INCLUDED */
diff --git a/deps/EXPAT/expat/expat_config.h b/deps/EXPAT/expat/expat_config.h
new file mode 100644
index 000000000..8aa80db0d
--- /dev/null
+++ b/deps/EXPAT/expat/expat_config.h
@@ -0,0 +1,33 @@
+/*================================================================
+** Copyright 2000, Clark Cooper
+** All rights reserved.
+**
+** This is free software. You are permitted to copy, distribute, or modify
+** it under the terms of the MIT/X license (contained in the COPYING file
+** with this distribution.)
+*/
+
+#ifndef EXPATCONFIG_H
+#define EXPATCONFIG_H
+
+#include <memory.h>
+#include <string.h>
+
+#define XML_NS 1
+#define XML_DTD 1
+#define XML_CONTEXT_BYTES 1024
+
+/* we will assume all Windows platforms are little endian */
+#define BYTEORDER 1234
+
+/* Windows has memmove() available. */
+#define HAVE_MEMMOVE
+
+#ifdef WIN32
+	#define WIN32_LEAN_AND_MEAN
+	#include <windows.h>
+	#undef WIN32_LEAN_AND_MEAN
+#else
+#endif
+
+#endif /* ifndef EXPATCONFIG_H */
diff --git a/deps/EXPAT/expat/expat_external.h b/deps/EXPAT/expat/expat_external.h
new file mode 100644
index 000000000..56cd84367
--- /dev/null
+++ b/deps/EXPAT/expat/expat_external.h
@@ -0,0 +1,129 @@
+/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+#ifndef Expat_External_INCLUDED
+#define Expat_External_INCLUDED 1
+
+/* External API definitions */
+
+#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__)
+#define XML_USE_MSC_EXTENSIONS 1
+#endif
+
+/* Expat tries very hard to make the API boundary very specifically
+   defined.  There are two macros defined to control this boundary;
+   each of these can be defined before including this header to
+   achieve some different behavior, but doing so it not recommended or
+   tested frequently.
+
+   XMLCALL    - The calling convention to use for all calls across the
+                "library boundary."  This will default to cdecl, and
+                try really hard to tell the compiler that's what we
+                want.
+
+   XMLIMPORT  - Whatever magic is needed to note that a function is
+                to be imported from a dynamically loaded library
+                (.dll, .so, or .sl, depending on your platform).
+
+   The XMLCALL macro was added in Expat 1.95.7.  The only one which is
+   expected to be directly useful in client code is XMLCALL.
+
+   Note that on at least some Unix versions, the Expat library must be
+   compiled with the cdecl calling convention as the default since
+   system headers may assume the cdecl convention.
+*/
+#ifndef XMLCALL
+#if defined(_MSC_VER)
+#define XMLCALL __cdecl
+#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER)
+#define XMLCALL __attribute__((cdecl))
+#else
+/* For any platform which uses this definition and supports more than
+   one calling convention, we need to extend this definition to
+   declare the convention used on that platform, if it's possible to
+   do so.
+
+   If this is the case for your platform, please file a bug report
+   with information on how to identify your platform via the C
+   pre-processor and how to specify the same calling convention as the
+   platform's malloc() implementation.
+*/
+#define XMLCALL
+#endif
+#endif  /* not defined XMLCALL */
+
+
+#if !defined(XML_STATIC) && !defined(XMLIMPORT)
+#ifndef XML_BUILDING_EXPAT
+/* using Expat from an application */
+
+#ifdef XML_USE_MSC_EXTENSIONS
+// #define XMLIMPORT __declspec(dllimport)
+#endif
+
+#endif
+#endif  /* not defined XML_STATIC */
+
+#if !defined(XMLIMPORT) && defined(__GNUC__) && (__GNUC__ >= 4)
+#define XMLIMPORT __attribute__ ((visibility ("default")))
+#endif
+
+/* If we didn't define it above, define it away: */
+#ifndef XMLIMPORT
+#define XMLIMPORT
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
+#define XML_ATTR_MALLOC __attribute__((__malloc__))
+#else
+#define XML_ATTR_MALLOC
+#endif
+
+#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+#define XML_ATTR_ALLOC_SIZE(x)  __attribute__((__alloc_size__(x)))
+#else
+#define XML_ATTR_ALLOC_SIZE(x)
+#endif
+
+#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef XML_UNICODE_WCHAR_T
+#define XML_UNICODE
+#endif
+
+#ifdef XML_UNICODE     /* Information is UTF-16 encoded. */
+#ifdef XML_UNICODE_WCHAR_T
+typedef wchar_t XML_Char;
+typedef wchar_t XML_LChar;
+#else
+typedef unsigned short XML_Char;
+typedef char XML_LChar;
+#endif /* XML_UNICODE_WCHAR_T */
+#else                  /* Information is UTF-8 encoded. */
+typedef char XML_Char;
+typedef char XML_LChar;
+#endif /* XML_UNICODE */
+
+#ifdef XML_LARGE_SIZE  /* Use large integers for file/stream positions. */
+#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400
+typedef __int64 XML_Index; 
+typedef unsigned __int64 XML_Size;
+#else
+typedef long long XML_Index;
+typedef unsigned long long XML_Size;
+#endif
+#else
+typedef long XML_Index;
+typedef unsigned long XML_Size;
+#endif /* XML_LARGE_SIZE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not Expat_External_INCLUDED */
diff --git a/deps/EXPAT/expat/iasciitab.h b/deps/EXPAT/expat/iasciitab.h
new file mode 100644
index 000000000..24a1d5ccc
--- /dev/null
+++ b/deps/EXPAT/expat/iasciitab.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/deps/EXPAT/expat/internal.h b/deps/EXPAT/expat/internal.h
new file mode 100644
index 000000000..94cb98e15
--- /dev/null
+++ b/deps/EXPAT/expat/internal.h
@@ -0,0 +1,95 @@
+/* internal.h
+
+   Internal definitions used by Expat.  This is not needed to compile
+   client code.
+
+   The following calling convention macros are defined for frequently
+   called functions:
+
+   FASTCALL    - Used for those internal functions that have a simple
+                 body and a low number of arguments and local variables.
+
+   PTRCALL     - Used for functions called though function pointers.
+
+   PTRFASTCALL - Like PTRCALL, but for low number of arguments.
+
+   inline      - Used for selected internal functions for which inlining
+                 may improve performance on some platforms.
+
+   Note: Use of these macros is based on judgement, not hard rules,
+         and therefore subject to change.
+*/
+
+#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__)
+/* We'll use this version by default only where we know it helps.
+
+   regparm() generates warnings on Solaris boxes.   See SF bug #692878.
+
+   Instability reported with egcs on a RedHat Linux 7.3.
+   Let's comment out:
+   #define FASTCALL __attribute__((stdcall, regparm(3)))
+   and let's try this:
+*/
+#define FASTCALL __attribute__((regparm(3)))
+#define PTRFASTCALL __attribute__((regparm(3)))
+#endif
+
+/* Using __fastcall seems to have an unexpected negative effect under
+   MS VC++, especially for function pointers, so we won't use it for
+   now on that platform. It may be reconsidered for a future release
+   if it can be made more effective.
+   Likely reason: __fastcall on Windows is like stdcall, therefore
+   the compiler cannot perform stack optimizations for call clusters.
+*/
+
+/* Make sure all of these are defined if they aren't already. */
+
+#ifndef FASTCALL
+#define FASTCALL
+#endif
+
+#ifndef PTRCALL
+#define PTRCALL
+#endif
+
+#ifndef PTRFASTCALL
+#define PTRFASTCALL
+#endif
+
+#ifndef XML_MIN_SIZE
+#if !defined(__cplusplus) && !defined(inline)
+#ifdef __GNUC__
+#define inline __inline
+#endif /* __GNUC__ */
+#endif
+#endif /* XML_MIN_SIZE */
+
+#ifdef __cplusplus
+#define inline inline
+#else
+#ifndef inline
+#define inline
+#endif
+#endif
+
+#ifndef UNUSED_P
+# ifdef __GNUC__
+#  define UNUSED_P(p) UNUSED_ ## p __attribute__((__unused__))
+# else
+#  define UNUSED_P(p) UNUSED_ ## p
+# endif
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+void
+align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/deps/EXPAT/expat/latin1tab.h b/deps/EXPAT/expat/latin1tab.h
new file mode 100644
index 000000000..53c25d76b
--- /dev/null
+++ b/deps/EXPAT/expat/latin1tab.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
+/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
diff --git a/deps/EXPAT/expat/nametab.h b/deps/EXPAT/expat/nametab.h
new file mode 100644
index 000000000..b05e62c77
--- /dev/null
+++ b/deps/EXPAT/expat/nametab.h
@@ -0,0 +1,150 @@
+static const unsigned namingBitmap[] = {
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF,
+0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000,
+0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
+0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003,
+0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003,
+0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
+0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001,
+0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003,
+0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000,
+0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003,
+0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003,
+0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000,
+0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000,
+0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF,
+0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB,
+0x40000000, 0xF580C900, 0x00000007, 0x02010800,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF,
+0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
+0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF,
+0x00000000, 0x00004C40, 0x00000000, 0x00000000,
+0x00000007, 0x00000000, 0x00000000, 0x00000000,
+0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF,
+0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000,
+0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003,
+0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF,
+0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF,
+0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
+0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF,
+0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0,
+0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1,
+0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3,
+0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80,
+0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000,
+0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000,
+0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF,
+0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x1FFF0000, 0x00000002,
+0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF,
+};
+static const unsigned char nmstrtPages[] = {
+0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,
+0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+static const unsigned char namePages[] = {
+0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00,
+0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/deps/EXPAT/expat/utf8tab.h b/deps/EXPAT/expat/utf8tab.h
new file mode 100644
index 000000000..7bb3e7760
--- /dev/null
+++ b/deps/EXPAT/expat/utf8tab.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+
+/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
+/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,
diff --git a/deps/EXPAT/expat/xmlparse.c b/deps/EXPAT/expat/xmlparse.c
new file mode 100644
index 000000000..fbe5e0200
--- /dev/null
+++ b/deps/EXPAT/expat/xmlparse.c
@@ -0,0 +1,6458 @@
+/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+#include <stddef.h>
+#include <string.h>                     /* memset(), memcpy() */
+#include <assert.h>
+#include <limits.h>                     /* UINT_MAX */
+
+#ifdef WIN32
+#define getpid GetCurrentProcessId
+#else
+#include <sys/time.h>                   /* gettimeofday() */
+#include <sys/types.h>                  /* getpid() */
+#include <unistd.h>                     /* getpid() */
+#endif
+
+#define XML_BUILDING_EXPAT 1
+
+#include "expat_config.h"
+#include "ascii.h"
+#include "expat.h"
+
+#ifdef XML_UNICODE
+#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
+#define XmlConvert XmlUtf16Convert
+#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS
+#define XmlEncode XmlUtf16Encode
+/* Using pointer subtraction to convert to integer type. */
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1))
+typedef unsigned short ICHAR;
+#else
+#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX
+#define XmlConvert XmlUtf8Convert
+#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS
+#define XmlEncode XmlUtf8Encode
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf8)
+typedef char ICHAR;
+#endif
+
+
+#ifndef XML_NS
+
+#define XmlInitEncodingNS XmlInitEncoding
+#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding
+#undef XmlGetInternalEncodingNS
+#define XmlGetInternalEncodingNS XmlGetInternalEncoding
+#define XmlParseXmlDeclNS XmlParseXmlDecl
+
+#endif
+
+#ifdef XML_UNICODE
+
+#ifdef XML_UNICODE_WCHAR_T
+#define XML_T(x) (const wchar_t)x
+#define XML_L(x) L ## x
+#else
+#define XML_T(x) (const unsigned short)x
+#define XML_L(x) x
+#endif
+
+#else
+
+#define XML_T(x) x
+#define XML_L(x) x
+
+#endif
+
+/* Round up n to be a multiple of sz, where sz is a power of 2. */
+#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
+
+/* Handle the case where memmove() doesn't exist. */
+#ifndef HAVE_MEMMOVE
+#ifdef HAVE_BCOPY
+#define memmove(d,s,l) bcopy((s),(d),(l))
+#else
+#error memmove does not exist on this platform, nor is a substitute available
+#endif /* HAVE_BCOPY */
+#endif /* HAVE_MEMMOVE */
+
+#include "internal.h"
+#include "xmltok.h"
+#include "xmlrole.h"
+
+typedef const XML_Char *KEY;
+
+typedef struct {
+  KEY name;
+} NAMED;
+
+typedef struct {
+  NAMED **v;
+  unsigned char power;
+  size_t size;
+  size_t used;
+  const XML_Memory_Handling_Suite *mem;
+} HASH_TABLE;
+
+/* Basic character hash algorithm, taken from Python's string hash:
+   h = h * 1000003 ^ character, the constant being a prime number.
+
+*/
+#ifdef XML_UNICODE
+#define CHAR_HASH(h, c) \
+  (((h) * 0xF4243) ^ (unsigned short)(c))
+#else
+#define CHAR_HASH(h, c) \
+  (((h) * 0xF4243) ^ (unsigned char)(c))
+#endif
+
+/* For probing (after a collision) we need a step size relative prime
+   to the hash table size, which is a power of 2. We use double-hashing,
+   since we can calculate a second hash value cheaply by taking those bits
+   of the first hash value that were discarded (masked out) when the table
+   index was calculated: index = hash & mask, where mask = table->size - 1.
+   We limit the maximum step size to table->size / 4 (mask >> 2) and make
+   it odd, since odd numbers are always relative prime to a power of 2.
+*/
+#define SECOND_HASH(hash, mask, power) \
+  ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2))
+#define PROBE_STEP(hash, mask, power) \
+  ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1))
+
+typedef struct {
+  NAMED **p;
+  NAMED **end;
+} HASH_TABLE_ITER;
+
+#define INIT_TAG_BUF_SIZE 32  /* must be a multiple of sizeof(XML_Char) */
+#define INIT_DATA_BUF_SIZE 1024
+#define INIT_ATTS_SIZE 16
+#define INIT_ATTS_VERSION 0xFFFFFFFF
+#define INIT_BLOCK_SIZE 1024
+#define INIT_BUFFER_SIZE 1024
+
+#define EXPAND_SPARE 24
+
+typedef struct binding {
+  struct prefix *prefix;
+  struct binding *nextTagBinding;
+  struct binding *prevPrefixBinding;
+  const struct attribute_id *attId;
+  XML_Char *uri;
+  int uriLen;
+  int uriAlloc;
+} BINDING;
+
+typedef struct prefix {
+  const XML_Char *name;
+  BINDING *binding;
+} PREFIX;
+
+typedef struct {
+  const XML_Char *str;
+  const XML_Char *localPart;
+  const XML_Char *prefix;
+  int strLen;
+  int uriLen;
+  int prefixLen;
+} TAG_NAME;
+
+/* TAG represents an open element.
+   The name of the element is stored in both the document and API
+   encodings.  The memory buffer 'buf' is a separately-allocated
+   memory area which stores the name.  During the XML_Parse()/
+   XMLParseBuffer() when the element is open, the memory for the 'raw'
+   version of the name (in the document encoding) is shared with the
+   document buffer.  If the element is open across calls to
+   XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to
+   contain the 'raw' name as well.
+
+   A parser re-uses these structures, maintaining a list of allocated
+   TAG objects in a free list.
+*/
+typedef struct tag {
+  struct tag *parent;           /* parent of this element */
+  const char *rawName;          /* tagName in the original encoding */
+  int rawNameLength;
+  TAG_NAME name;                /* tagName in the API encoding */
+  char *buf;                    /* buffer for name components */
+  char *bufEnd;                 /* end of the buffer */
+  BINDING *bindings;
+} TAG;
+
+typedef struct {
+  const XML_Char *name;
+  const XML_Char *textPtr;
+  int textLen;                  /* length in XML_Chars */
+  int processed;                /* # of processed bytes - when suspended */
+  const XML_Char *systemId;
+  const XML_Char *base;
+  const XML_Char *publicId;
+  const XML_Char *notation;
+  XML_Bool open;
+  XML_Bool is_param;
+  XML_Bool is_internal; /* true if declared in internal subset outside PE */
+} ENTITY;
+
+typedef struct {
+  enum XML_Content_Type         type;
+  enum XML_Content_Quant        quant;
+  const XML_Char *              name;
+  int                           firstchild;
+  int                           lastchild;
+  int                           childcnt;
+  int                           nextsib;
+} CONTENT_SCAFFOLD;
+
+#define INIT_SCAFFOLD_ELEMENTS 32
+
+typedef struct block {
+  struct block *next;
+  int size;
+  XML_Char s[1];
+} BLOCK;
+
+typedef struct {
+  BLOCK *blocks;
+  BLOCK *freeBlocks;
+  const XML_Char *end;
+  XML_Char *ptr;
+  XML_Char *start;
+  const XML_Memory_Handling_Suite *mem;
+} STRING_POOL;
+
+/* The XML_Char before the name is used to determine whether
+   an attribute has been specified. */
+typedef struct attribute_id {
+  XML_Char *name;
+  PREFIX *prefix;
+  XML_Bool maybeTokenized;
+  XML_Bool xmlns;
+} ATTRIBUTE_ID;
+
+typedef struct {
+  const ATTRIBUTE_ID *id;
+  XML_Bool isCdata;
+  const XML_Char *value;
+} DEFAULT_ATTRIBUTE;
+
+typedef struct {
+  unsigned long version;
+  unsigned long hash;
+  const XML_Char *uriName;
+} NS_ATT;
+
+typedef struct {
+  const XML_Char *name;
+  PREFIX *prefix;
+  const ATTRIBUTE_ID *idAtt;
+  int nDefaultAtts;
+  int allocDefaultAtts;
+  DEFAULT_ATTRIBUTE *defaultAtts;
+} ELEMENT_TYPE;
+
+typedef struct {
+  HASH_TABLE generalEntities;
+  HASH_TABLE elementTypes;
+  HASH_TABLE attributeIds;
+  HASH_TABLE prefixes;
+  STRING_POOL pool;
+  STRING_POOL entityValuePool;
+  /* false once a parameter entity reference has been skipped */
+  XML_Bool keepProcessing;
+  /* true once an internal or external PE reference has been encountered;
+     this includes the reference to an external subset */
+  XML_Bool hasParamEntityRefs;
+  XML_Bool standalone;
+#ifdef XML_DTD
+  /* indicates if external PE has been read */
+  XML_Bool paramEntityRead;
+  HASH_TABLE paramEntities;
+#endif /* XML_DTD */
+  PREFIX defaultPrefix;
+  /* === scaffolding for building content model === */
+  XML_Bool in_eldecl;
+  CONTENT_SCAFFOLD *scaffold;
+  unsigned contentStringLen;
+  unsigned scaffSize;
+  unsigned scaffCount;
+  int scaffLevel;
+  int *scaffIndex;
+} DTD;
+
+typedef struct open_internal_entity {
+  const char *internalEventPtr;
+  const char *internalEventEndPtr;
+  struct open_internal_entity *next;
+  ENTITY *entity;
+  int startTagLevel;
+  XML_Bool betweenDecl; /* WFC: PE Between Declarations */
+} OPEN_INTERNAL_ENTITY;
+
+typedef enum XML_Error PTRCALL Processor(XML_Parser parser,
+                                         const char *start,
+                                         const char *end,
+                                         const char **endPtr);
+
+static Processor prologProcessor;
+static Processor prologInitProcessor;
+static Processor contentProcessor;
+static Processor cdataSectionProcessor;
+#ifdef XML_DTD
+static Processor ignoreSectionProcessor;
+static Processor externalParEntProcessor;
+static Processor externalParEntInitProcessor;
+static Processor entityValueProcessor;
+static Processor entityValueInitProcessor;
+#endif /* XML_DTD */
+static Processor epilogProcessor;
+static Processor errorProcessor;
+static Processor externalEntityInitProcessor;
+static Processor externalEntityInitProcessor2;
+static Processor externalEntityInitProcessor3;
+static Processor externalEntityContentProcessor;
+static Processor internalEntityProcessor;
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName);
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
+               const char *s, const char *next);
+static enum XML_Error
+initializeEncoding(XML_Parser parser);
+static enum XML_Error
+doProlog(XML_Parser parser, const ENCODING *enc, const char *s,
+         const char *end, int tok, const char *next, const char **nextPtr,
+         XML_Bool haveMore);
+static enum XML_Error
+processInternalEntity(XML_Parser parser, ENTITY *entity,
+                      XML_Bool betweenDecl);
+static enum XML_Error
+doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+          const char *start, const char *end, const char **endPtr,
+          XML_Bool haveMore);
+static enum XML_Error
+doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr,
+               const char *end, const char **nextPtr, XML_Bool haveMore);
+#ifdef XML_DTD
+static enum XML_Error
+doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr,
+                const char *end, const char **nextPtr, XML_Bool haveMore);
+#endif /* XML_DTD */
+
+static enum XML_Error
+storeAtts(XML_Parser parser, const ENCODING *, const char *s,
+          TAG_NAME *tagNamePtr, BINDING **bindingsPtr);
+static enum XML_Error
+addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
+           const XML_Char *uri, BINDING **bindingsPtr);
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata,
+                XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser);
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata,
+                    const char *, const char *, STRING_POOL *);
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata,
+                     const char *, const char *, STRING_POOL *);
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start,
+               const char *end);
+static int
+setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
+static enum XML_Error
+storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start,
+                 const char *end);
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
+                            const char *start, const char *end);
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start,
+              const char *end);
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *start,
+              const char *end);
+
+static const XML_Char * getContext(XML_Parser parser);
+static XML_Bool
+setContext(XML_Parser parser, const XML_Char *context);
+
+static void FASTCALL normalizePublicId(XML_Char *s);
+
+static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms);
+/* do not call if parentParser != NULL */
+static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
+static void
+dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms);
+static int
+dtdCopy(XML_Parser oldParser,
+        DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms);
+static int
+copyEntityTable(XML_Parser oldParser,
+                HASH_TABLE *, STRING_POOL *, const HASH_TABLE *);
+static NAMED *
+lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize);
+static void FASTCALL
+hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms);
+static void FASTCALL hashTableClear(HASH_TABLE *);
+static void FASTCALL hashTableDestroy(HASH_TABLE *);
+static void FASTCALL
+hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
+static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *);
+
+static void FASTCALL
+poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms);
+static void FASTCALL poolClear(STRING_POOL *);
+static void FASTCALL poolDestroy(STRING_POOL *);
+static XML_Char *
+poolAppend(STRING_POOL *pool, const ENCODING *enc,
+           const char *ptr, const char *end);
+static XML_Char *
+poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+                const char *ptr, const char *end);
+static XML_Bool FASTCALL poolGrow(STRING_POOL *pool);
+static const XML_Char * FASTCALL
+poolCopyString(STRING_POOL *pool, const XML_Char *s);
+static const XML_Char *
+poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n);
+static const XML_Char * FASTCALL
+poolAppendString(STRING_POOL *pool, const XML_Char *s);
+
+static int FASTCALL nextScaffoldPart(XML_Parser parser);
+static XML_Content * build_model(XML_Parser parser);
+static ELEMENT_TYPE *
+getElementType(XML_Parser parser, const ENCODING *enc,
+               const char *ptr, const char *end);
+
+static unsigned long generate_hash_secret_salt(XML_Parser parser);
+static XML_Bool startParsing(XML_Parser parser);
+
+static XML_Parser
+parserCreate(const XML_Char *encodingName,
+             const XML_Memory_Handling_Suite *memsuite,
+             const XML_Char *nameSep,
+             DTD *dtd);
+
+static void
+parserInit(XML_Parser parser, const XML_Char *encodingName);
+
+#define poolStart(pool) ((pool)->start)
+#define poolEnd(pool) ((pool)->ptr)
+#define poolLength(pool) ((pool)->ptr - (pool)->start)
+#define poolChop(pool) ((void)--(pool->ptr))
+#define poolLastChar(pool) (((pool)->ptr)[-1])
+#define poolDiscard(pool) ((pool)->ptr = (pool)->start)
+#define poolFinish(pool) ((pool)->start = (pool)->ptr)
+#define poolAppendChar(pool, c) \
+  (((pool)->ptr == (pool)->end && !poolGrow(pool)) \
+   ? 0 \
+   : ((*((pool)->ptr)++ = c), 1))
+
+struct XML_ParserStruct {
+  /* The first member must be userData so that the XML_GetUserData
+     macro works. */
+  void *m_userData;
+  void *m_handlerArg;
+  char *m_buffer;
+  const XML_Memory_Handling_Suite m_mem;
+  /* first character to be parsed */
+  const char *m_bufferPtr;
+  /* past last character to be parsed */
+  char *m_bufferEnd;
+  /* allocated end of buffer */
+  const char *m_bufferLim;
+  XML_Index m_parseEndByteIndex;
+  const char *m_parseEndPtr;
+  XML_Char *m_dataBuf;
+  XML_Char *m_dataBufEnd;
+  XML_StartElementHandler m_startElementHandler;
+  XML_EndElementHandler m_endElementHandler;
+  XML_CharacterDataHandler m_characterDataHandler;
+  XML_ProcessingInstructionHandler m_processingInstructionHandler;
+  XML_CommentHandler m_commentHandler;
+  XML_StartCdataSectionHandler m_startCdataSectionHandler;
+  XML_EndCdataSectionHandler m_endCdataSectionHandler;
+  XML_DefaultHandler m_defaultHandler;
+  XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler;
+  XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler;
+  XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler;
+  XML_NotationDeclHandler m_notationDeclHandler;
+  XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler;
+  XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler;
+  XML_NotStandaloneHandler m_notStandaloneHandler;
+  XML_ExternalEntityRefHandler m_externalEntityRefHandler;
+  XML_Parser m_externalEntityRefHandlerArg;
+  XML_SkippedEntityHandler m_skippedEntityHandler;
+  XML_UnknownEncodingHandler m_unknownEncodingHandler;
+  XML_ElementDeclHandler m_elementDeclHandler;
+  XML_AttlistDeclHandler m_attlistDeclHandler;
+  XML_EntityDeclHandler m_entityDeclHandler;
+  XML_XmlDeclHandler m_xmlDeclHandler;
+  const ENCODING *m_encoding;
+  INIT_ENCODING m_initEncoding;
+  const ENCODING *m_internalEncoding;
+  const XML_Char *m_protocolEncodingName;
+  XML_Bool m_ns;
+  XML_Bool m_ns_triplets;
+  void *m_unknownEncodingMem;
+  void *m_unknownEncodingData;
+  void *m_unknownEncodingHandlerData;
+  void (XMLCALL *m_unknownEncodingRelease)(void *);
+  PROLOG_STATE m_prologState;
+  Processor *m_processor;
+  enum XML_Error m_errorCode;
+  const char *m_eventPtr;
+  const char *m_eventEndPtr;
+  const char *m_positionPtr;
+  OPEN_INTERNAL_ENTITY *m_openInternalEntities;
+  OPEN_INTERNAL_ENTITY *m_freeInternalEntities;
+  XML_Bool m_defaultExpandInternalEntities;
+  int m_tagLevel;
+  ENTITY *m_declEntity;
+  const XML_Char *m_doctypeName;
+  const XML_Char *m_doctypeSysid;
+  const XML_Char *m_doctypePubid;
+  const XML_Char *m_declAttributeType;
+  const XML_Char *m_declNotationName;
+  const XML_Char *m_declNotationPublicId;
+  ELEMENT_TYPE *m_declElementType;
+  ATTRIBUTE_ID *m_declAttributeId;
+  XML_Bool m_declAttributeIsCdata;
+  XML_Bool m_declAttributeIsId;
+  DTD *m_dtd;
+  const XML_Char *m_curBase;
+  TAG *m_tagStack;
+  TAG *m_freeTagList;
+  BINDING *m_inheritedBindings;
+  BINDING *m_freeBindingList;
+  int m_attsSize;
+  int m_nSpecifiedAtts;
+  int m_idAttIndex;
+  ATTRIBUTE *m_atts;
+  NS_ATT *m_nsAtts;
+  unsigned long m_nsAttsVersion;
+  unsigned char m_nsAttsPower;
+#ifdef XML_ATTR_INFO
+  XML_AttrInfo *m_attInfo;
+#endif
+  POSITION m_position;
+  STRING_POOL m_tempPool;
+  STRING_POOL m_temp2Pool;
+  char *m_groupConnector;
+  unsigned int m_groupSize;
+  XML_Char m_namespaceSeparator;
+  XML_Parser m_parentParser;
+  XML_ParsingStatus m_parsingStatus;
+#ifdef XML_DTD
+  XML_Bool m_isParamEntity;
+  XML_Bool m_useForeignDTD;
+  enum XML_ParamEntityParsing m_paramEntityParsing;
+#endif
+  unsigned long m_hash_secret_salt;
+};
+
+#define MALLOC(s) (parser->m_mem.malloc_fcn((s)))
+#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s)))
+#define FREE(p) (parser->m_mem.free_fcn((p)))
+
+#define userData (parser->m_userData)
+#define handlerArg (parser->m_handlerArg)
+#define startElementHandler (parser->m_startElementHandler)
+#define endElementHandler (parser->m_endElementHandler)
+#define characterDataHandler (parser->m_characterDataHandler)
+#define processingInstructionHandler \
+        (parser->m_processingInstructionHandler)
+#define commentHandler (parser->m_commentHandler)
+#define startCdataSectionHandler \
+        (parser->m_startCdataSectionHandler)
+#define endCdataSectionHandler (parser->m_endCdataSectionHandler)
+#define defaultHandler (parser->m_defaultHandler)
+#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler)
+#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler)
+#define unparsedEntityDeclHandler \
+        (parser->m_unparsedEntityDeclHandler)
+#define notationDeclHandler (parser->m_notationDeclHandler)
+#define startNamespaceDeclHandler \
+        (parser->m_startNamespaceDeclHandler)
+#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler)
+#define notStandaloneHandler (parser->m_notStandaloneHandler)
+#define externalEntityRefHandler \
+        (parser->m_externalEntityRefHandler)
+#define externalEntityRefHandlerArg \
+        (parser->m_externalEntityRefHandlerArg)
+#define internalEntityRefHandler \
+        (parser->m_internalEntityRefHandler)
+#define skippedEntityHandler (parser->m_skippedEntityHandler)
+#define unknownEncodingHandler (parser->m_unknownEncodingHandler)
+#define elementDeclHandler (parser->m_elementDeclHandler)
+#define attlistDeclHandler (parser->m_attlistDeclHandler)
+#define entityDeclHandler (parser->m_entityDeclHandler)
+#define xmlDeclHandler (parser->m_xmlDeclHandler)
+#define encoding (parser->m_encoding)
+#define initEncoding (parser->m_initEncoding)
+#define internalEncoding (parser->m_internalEncoding)
+#define unknownEncodingMem (parser->m_unknownEncodingMem)
+#define unknownEncodingData (parser->m_unknownEncodingData)
+#define unknownEncodingHandlerData \
+  (parser->m_unknownEncodingHandlerData)
+#define unknownEncodingRelease (parser->m_unknownEncodingRelease)
+#define protocolEncodingName (parser->m_protocolEncodingName)
+#define ns (parser->m_ns)
+#define ns_triplets (parser->m_ns_triplets)
+#define prologState (parser->m_prologState)
+#define processor (parser->m_processor)
+#define errorCode (parser->m_errorCode)
+#define eventPtr (parser->m_eventPtr)
+#define eventEndPtr (parser->m_eventEndPtr)
+#define positionPtr (parser->m_positionPtr)
+#define position (parser->m_position)
+#define openInternalEntities (parser->m_openInternalEntities)
+#define freeInternalEntities (parser->m_freeInternalEntities)
+#define defaultExpandInternalEntities \
+        (parser->m_defaultExpandInternalEntities)
+#define tagLevel (parser->m_tagLevel)
+#define buffer (parser->m_buffer)
+#define bufferPtr (parser->m_bufferPtr)
+#define bufferEnd (parser->m_bufferEnd)
+#define parseEndByteIndex (parser->m_parseEndByteIndex)
+#define parseEndPtr (parser->m_parseEndPtr)
+#define bufferLim (parser->m_bufferLim)
+#define dataBuf (parser->m_dataBuf)
+#define dataBufEnd (parser->m_dataBufEnd)
+#define _dtd (parser->m_dtd)
+#define curBase (parser->m_curBase)
+#define declEntity (parser->m_declEntity)
+#define doctypeName (parser->m_doctypeName)
+#define doctypeSysid (parser->m_doctypeSysid)
+#define doctypePubid (parser->m_doctypePubid)
+#define declAttributeType (parser->m_declAttributeType)
+#define declNotationName (parser->m_declNotationName)
+#define declNotationPublicId (parser->m_declNotationPublicId)
+#define declElementType (parser->m_declElementType)
+#define declAttributeId (parser->m_declAttributeId)
+#define declAttributeIsCdata (parser->m_declAttributeIsCdata)
+#define declAttributeIsId (parser->m_declAttributeIsId)
+#define freeTagList (parser->m_freeTagList)
+#define freeBindingList (parser->m_freeBindingList)
+#define inheritedBindings (parser->m_inheritedBindings)
+#define tagStack (parser->m_tagStack)
+#define atts (parser->m_atts)
+#define attsSize (parser->m_attsSize)
+#define nSpecifiedAtts (parser->m_nSpecifiedAtts)
+#define idAttIndex (parser->m_idAttIndex)
+#define nsAtts (parser->m_nsAtts)
+#define nsAttsVersion (parser->m_nsAttsVersion)
+#define nsAttsPower (parser->m_nsAttsPower)
+#define attInfo (parser->m_attInfo)
+#define tempPool (parser->m_tempPool)
+#define temp2Pool (parser->m_temp2Pool)
+#define groupConnector (parser->m_groupConnector)
+#define groupSize (parser->m_groupSize)
+#define namespaceSeparator (parser->m_namespaceSeparator)
+#define parentParser (parser->m_parentParser)
+#define ps_parsing (parser->m_parsingStatus.parsing)
+#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer)
+#ifdef XML_DTD
+#define isParamEntity (parser->m_isParamEntity)
+#define useForeignDTD (parser->m_useForeignDTD)
+#define paramEntityParsing (parser->m_paramEntityParsing)
+#endif /* XML_DTD */
+#define hash_secret_salt (parser->m_hash_secret_salt)
+
+XML_Parser XMLCALL
+XML_ParserCreate(const XML_Char *encodingName)
+{
+  return XML_ParserCreate_MM(encodingName, NULL, NULL);
+}
+
+XML_Parser XMLCALL
+XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep)
+{
+  XML_Char tmp[2];
+  *tmp = nsSep;
+  return XML_ParserCreate_MM(encodingName, NULL, tmp);
+}
+
+static const XML_Char implicitContext[] = {
+  ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p,
+  ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w,
+  ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g,
+  ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9,
+  ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e,
+  ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0'
+};
+
+static unsigned long
+gather_time_entropy(void)
+{
+#ifdef WIN32
+  FILETIME ft;
+  GetSystemTimeAsFileTime(&ft); /* never fails */
+  return ft.dwHighDateTime ^ ft.dwLowDateTime;
+#else
+  struct timeval tv;
+  int gettimeofday_res;
+
+  gettimeofday_res = gettimeofday(&tv, NULL);
+  assert (gettimeofday_res == 0);
+
+  /* Microseconds time is <20 bits entropy */
+  return tv.tv_usec;
+#endif
+}
+
+static unsigned long
+generate_hash_secret_salt(XML_Parser parser)
+{
+  /* Process ID is 0 bits entropy if attacker has local access
+   * XML_Parser address is few bits of entropy if attacker has local access */
+  // Prusa3D specific: Fix for a following warning, which turns to an error on some Perl/XS installations:
+  // error: cast from 'XML_Parser' to 'long unsigned int' loses precision [-fpermissive]
+  unsigned long *parser_addr = (unsigned long*)&parser;
+  const unsigned long entropy =
+      gather_time_entropy() ^ getpid() ^ *parser_addr;
+
+  /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */
+  if (sizeof(unsigned long) == 4) {
+    return entropy * 2147483647;
+  } else {
+    return entropy * (unsigned long)2305843009213693951;
+  }
+}
+
+static XML_Bool  /* only valid for root parser */
+startParsing(XML_Parser parser)
+{
+    /* hash functions must be initialized before setContext() is called */
+    if (hash_secret_salt == 0)
+      hash_secret_salt = generate_hash_secret_salt(parser);
+    if (ns) {
+      /* implicit context only set for root parser, since child
+         parsers (i.e. external entity parsers) will inherit it
+      */
+      return setContext(parser, implicitContext);
+    }
+    return XML_TRUE;
+}
+
+XML_Parser XMLCALL
+XML_ParserCreate_MM(const XML_Char *encodingName,
+                    const XML_Memory_Handling_Suite *memsuite,
+                    const XML_Char *nameSep)
+{
+  return parserCreate(encodingName, memsuite, nameSep, NULL);
+}
+
+static XML_Parser
+parserCreate(const XML_Char *encodingName,
+             const XML_Memory_Handling_Suite *memsuite,
+             const XML_Char *nameSep,
+             DTD *dtd)
+{
+  XML_Parser parser;
+
+  if (memsuite) {
+    XML_Memory_Handling_Suite *mtemp;
+    parser = (XML_Parser)
+      memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
+    if (parser != NULL) {
+      mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
+      mtemp->malloc_fcn = memsuite->malloc_fcn;
+      mtemp->realloc_fcn = memsuite->realloc_fcn;
+      mtemp->free_fcn = memsuite->free_fcn;
+    }
+  }
+  else {
+    XML_Memory_Handling_Suite *mtemp;
+    parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
+    if (parser != NULL) {
+      mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
+      mtemp->malloc_fcn = malloc;
+      mtemp->realloc_fcn = realloc;
+      mtemp->free_fcn = free;
+    }
+  }
+
+  if (!parser)
+    return parser;
+
+  buffer = NULL;
+  bufferLim = NULL;
+
+  attsSize = INIT_ATTS_SIZE;
+  atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE));
+  if (atts == NULL) {
+    FREE(parser);
+    return NULL;
+  }
+#ifdef XML_ATTR_INFO
+  attInfo = (XML_AttrInfo*)MALLOC(attsSize * sizeof(XML_AttrInfo));
+  if (attInfo == NULL) {
+    FREE(atts);
+    FREE(parser);
+    return NULL;
+  }
+#endif
+  dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char));
+  if (dataBuf == NULL) {
+    FREE(atts);
+#ifdef XML_ATTR_INFO
+    FREE(attInfo);
+#endif
+    FREE(parser);
+    return NULL;
+  }
+  dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE;
+
+  if (dtd)
+    _dtd = dtd;
+  else {
+    _dtd = dtdCreate(&parser->m_mem);
+    if (_dtd == NULL) {
+      FREE(dataBuf);
+      FREE(atts);
+#ifdef XML_ATTR_INFO
+      FREE(attInfo);
+#endif
+      FREE(parser);
+      return NULL;
+    }
+  }
+
+  freeBindingList = NULL;
+  freeTagList = NULL;
+  freeInternalEntities = NULL;
+
+  groupSize = 0;
+  groupConnector = NULL;
+
+  unknownEncodingHandler = NULL;
+  unknownEncodingHandlerData = NULL;
+
+  namespaceSeparator = ASCII_EXCL;
+  ns = XML_FALSE;
+  ns_triplets = XML_FALSE;
+
+  nsAtts = NULL;
+  nsAttsVersion = 0;
+  nsAttsPower = 0;
+
+  poolInit(&tempPool, &(parser->m_mem));
+  poolInit(&temp2Pool, &(parser->m_mem));
+  parserInit(parser, encodingName);
+
+  if (encodingName && !protocolEncodingName) {
+    XML_ParserFree(parser);
+    return NULL;
+  }
+
+  if (nameSep) {
+    ns = XML_TRUE;
+    internalEncoding = XmlGetInternalEncodingNS();
+    namespaceSeparator = *nameSep;
+  }
+  else {
+    internalEncoding = XmlGetInternalEncoding();
+  }
+
+  return parser;
+}
+
+static void
+parserInit(XML_Parser parser, const XML_Char *encodingName)
+{
+  processor = prologInitProcessor;
+  XmlPrologStateInit(&prologState);
+  protocolEncodingName = (encodingName != NULL
+                          ? poolCopyString(&tempPool, encodingName)
+                          : NULL);
+  curBase = NULL;
+  XmlInitEncoding(&initEncoding, &encoding, 0);
+  userData = NULL;
+  handlerArg = NULL;
+  startElementHandler = NULL;
+  endElementHandler = NULL;
+  characterDataHandler = NULL;
+  processingInstructionHandler = NULL;
+  commentHandler = NULL;
+  startCdataSectionHandler = NULL;
+  endCdataSectionHandler = NULL;
+  defaultHandler = NULL;
+  startDoctypeDeclHandler = NULL;
+  endDoctypeDeclHandler = NULL;
+  unparsedEntityDeclHandler = NULL;
+  notationDeclHandler = NULL;
+  startNamespaceDeclHandler = NULL;
+  endNamespaceDeclHandler = NULL;
+  notStandaloneHandler = NULL;
+  externalEntityRefHandler = NULL;
+  externalEntityRefHandlerArg = parser;
+  skippedEntityHandler = NULL;
+  elementDeclHandler = NULL;
+  attlistDeclHandler = NULL;
+  entityDeclHandler = NULL;
+  xmlDeclHandler = NULL;
+  bufferPtr = buffer;
+  bufferEnd = buffer;
+  parseEndByteIndex = 0;
+  parseEndPtr = NULL;
+  declElementType = NULL;
+  declAttributeId = NULL;
+  declEntity = NULL;
+  doctypeName = NULL;
+  doctypeSysid = NULL;
+  doctypePubid = NULL;
+  declAttributeType = NULL;
+  declNotationName = NULL;
+  declNotationPublicId = NULL;
+  declAttributeIsCdata = XML_FALSE;
+  declAttributeIsId = XML_FALSE;
+  memset(&position, 0, sizeof(POSITION));
+  errorCode = XML_ERROR_NONE;
+  eventPtr = NULL;
+  eventEndPtr = NULL;
+  positionPtr = NULL;
+  openInternalEntities = NULL;
+  defaultExpandInternalEntities = XML_TRUE;
+  tagLevel = 0;
+  tagStack = NULL;
+  inheritedBindings = NULL;
+  nSpecifiedAtts = 0;
+  unknownEncodingMem = NULL;
+  unknownEncodingRelease = NULL;
+  unknownEncodingData = NULL;
+  parentParser = NULL;
+  ps_parsing = XML_INITIALIZED;
+#ifdef XML_DTD
+  isParamEntity = XML_FALSE;
+  useForeignDTD = XML_FALSE;
+  paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+#endif
+  hash_secret_salt = 0;
+}
+
+/* moves list of bindings to freeBindingList */
+static void FASTCALL
+moveToFreeBindingList(XML_Parser parser, BINDING *bindings)
+{
+  while (bindings) {
+    BINDING *b = bindings;
+    bindings = bindings->nextTagBinding;
+    b->nextTagBinding = freeBindingList;
+    freeBindingList = b;
+  }
+}
+
+XML_Bool XMLCALL
+XML_ParserReset(XML_Parser parser, const XML_Char *encodingName)
+{
+  TAG *tStk;
+  OPEN_INTERNAL_ENTITY *openEntityList;
+  if (parentParser)
+    return XML_FALSE;
+  /* move tagStack to freeTagList */
+  tStk = tagStack;
+  while (tStk) {
+    TAG *tag = tStk;
+    tStk = tStk->parent;
+    tag->parent = freeTagList;
+    moveToFreeBindingList(parser, tag->bindings);
+    tag->bindings = NULL;
+    freeTagList = tag;
+  }
+  /* move openInternalEntities to freeInternalEntities */
+  openEntityList = openInternalEntities;
+  while (openEntityList) {
+    OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
+    openEntityList = openEntity->next;
+    openEntity->next = freeInternalEntities;
+    freeInternalEntities = openEntity;
+  }
+  moveToFreeBindingList(parser, inheritedBindings);
+  FREE(unknownEncodingMem);
+  if (unknownEncodingRelease)
+    unknownEncodingRelease(unknownEncodingData);
+  poolClear(&tempPool);
+  poolClear(&temp2Pool);
+  parserInit(parser, encodingName);
+  dtdReset(_dtd, &parser->m_mem);
+  return XML_TRUE;
+}
+
+enum XML_Status XMLCALL
+XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName)
+{
+  /* Block after XML_Parse()/XML_ParseBuffer() has been called.
+     XXX There's no way for the caller to determine which of the
+     XXX possible error cases caused the XML_STATUS_ERROR return.
+  */
+  if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+    return XML_STATUS_ERROR;
+  if (encodingName == NULL)
+    protocolEncodingName = NULL;
+  else {
+    protocolEncodingName = poolCopyString(&tempPool, encodingName);
+    if (!protocolEncodingName)
+      return XML_STATUS_ERROR;
+  }
+  return XML_STATUS_OK;
+}
+
+XML_Parser XMLCALL
+XML_ExternalEntityParserCreate(XML_Parser oldParser,
+                               const XML_Char *context,
+                               const XML_Char *encodingName)
+{
+  XML_Parser parser = oldParser;
+  DTD *newDtd = NULL;
+  DTD *oldDtd = _dtd;
+  XML_StartElementHandler oldStartElementHandler = startElementHandler;
+  XML_EndElementHandler oldEndElementHandler = endElementHandler;
+  XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler;
+  XML_ProcessingInstructionHandler oldProcessingInstructionHandler
+      = processingInstructionHandler;
+  XML_CommentHandler oldCommentHandler = commentHandler;
+  XML_StartCdataSectionHandler oldStartCdataSectionHandler
+      = startCdataSectionHandler;
+  XML_EndCdataSectionHandler oldEndCdataSectionHandler
+      = endCdataSectionHandler;
+  XML_DefaultHandler oldDefaultHandler = defaultHandler;
+  XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler
+      = unparsedEntityDeclHandler;
+  XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler;
+  XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler
+      = startNamespaceDeclHandler;
+  XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler
+      = endNamespaceDeclHandler;
+  XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler;
+  XML_ExternalEntityRefHandler oldExternalEntityRefHandler
+      = externalEntityRefHandler;
+  XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler;
+  XML_UnknownEncodingHandler oldUnknownEncodingHandler
+      = unknownEncodingHandler;
+  XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler;
+  XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler;
+  XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler;
+  XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler;
+  ELEMENT_TYPE * oldDeclElementType = declElementType;
+
+  void *oldUserData = userData;
+  void *oldHandlerArg = handlerArg;
+  XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities;
+  XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg;
+#ifdef XML_DTD
+  enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing;
+  int oldInEntityValue = prologState.inEntityValue;
+#endif
+  XML_Bool oldns_triplets = ns_triplets;
+  /* Note that the new parser shares the same hash secret as the old
+     parser, so that dtdCopy and copyEntityTable can lookup values
+     from hash tables associated with either parser without us having
+     to worry which hash secrets each table has.
+  */
+  unsigned long oldhash_secret_salt = hash_secret_salt;
+
+#ifdef XML_DTD
+  if (!context)
+    newDtd = oldDtd;
+#endif /* XML_DTD */
+
+  /* Note that the magical uses of the pre-processor to make field
+     access look more like C++ require that `parser' be overwritten
+     here.  This makes this function more painful to follow than it
+     would be otherwise.
+  */
+  if (ns) {
+    XML_Char tmp[2];
+    *tmp = namespaceSeparator;
+    parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
+  }
+  else {
+    parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
+  }
+
+  if (!parser)
+    return NULL;
+
+  startElementHandler = oldStartElementHandler;
+  endElementHandler = oldEndElementHandler;
+  characterDataHandler = oldCharacterDataHandler;
+  processingInstructionHandler = oldProcessingInstructionHandler;
+  commentHandler = oldCommentHandler;
+  startCdataSectionHandler = oldStartCdataSectionHandler;
+  endCdataSectionHandler = oldEndCdataSectionHandler;
+  defaultHandler = oldDefaultHandler;
+  unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler;
+  notationDeclHandler = oldNotationDeclHandler;
+  startNamespaceDeclHandler = oldStartNamespaceDeclHandler;
+  endNamespaceDeclHandler = oldEndNamespaceDeclHandler;
+  notStandaloneHandler = oldNotStandaloneHandler;
+  externalEntityRefHandler = oldExternalEntityRefHandler;
+  skippedEntityHandler = oldSkippedEntityHandler;
+  unknownEncodingHandler = oldUnknownEncodingHandler;
+  elementDeclHandler = oldElementDeclHandler;
+  attlistDeclHandler = oldAttlistDeclHandler;
+  entityDeclHandler = oldEntityDeclHandler;
+  xmlDeclHandler = oldXmlDeclHandler;
+  declElementType = oldDeclElementType;
+  userData = oldUserData;
+  if (oldUserData == oldHandlerArg)
+    handlerArg = userData;
+  else
+    handlerArg = parser;
+  if (oldExternalEntityRefHandlerArg != oldParser)
+    externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg;
+  defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
+  ns_triplets = oldns_triplets;
+  hash_secret_salt = oldhash_secret_salt;
+  parentParser = oldParser;
+#ifdef XML_DTD
+  paramEntityParsing = oldParamEntityParsing;
+  prologState.inEntityValue = oldInEntityValue;
+  if (context) {
+#endif /* XML_DTD */
+    if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem)
+      || !setContext(parser, context)) {
+      XML_ParserFree(parser);
+      return NULL;
+    }
+    processor = externalEntityInitProcessor;
+#ifdef XML_DTD
+  }
+  else {
+    /* The DTD instance referenced by _dtd is shared between the document's
+       root parser and external PE parsers, therefore one does not need to
+       call setContext. In addition, one also *must* not call setContext,
+       because this would overwrite existing prefix->binding pointers in
+       _dtd with ones that get destroyed with the external PE parser.
+       This would leave those prefixes with dangling pointers.
+    */
+    isParamEntity = XML_TRUE;
+    XmlPrologStateInitExternalEntity(&prologState);
+    processor = externalParEntInitProcessor;
+  }
+#endif /* XML_DTD */
+  return parser;
+}
+
+static void FASTCALL
+destroyBindings(BINDING *bindings, XML_Parser parser)
+{
+  for (;;) {
+    BINDING *b = bindings;
+    if (!b)
+      break;
+    bindings = b->nextTagBinding;
+    FREE(b->uri);
+    FREE(b);
+  }
+}
+
+void XMLCALL
+XML_ParserFree(XML_Parser parser)
+{
+  TAG *tagList;
+  OPEN_INTERNAL_ENTITY *entityList;
+  if (parser == NULL)
+    return;
+  /* free tagStack and freeTagList */
+  tagList = tagStack;
+  for (;;) {
+    TAG *p;
+    if (tagList == NULL) {
+      if (freeTagList == NULL)
+        break;
+      tagList = freeTagList;
+      freeTagList = NULL;
+    }
+    p = tagList;
+    tagList = tagList->parent;
+    FREE(p->buf);
+    destroyBindings(p->bindings, parser);
+    FREE(p);
+  }
+  /* free openInternalEntities and freeInternalEntities */
+  entityList = openInternalEntities;
+  for (;;) {
+    OPEN_INTERNAL_ENTITY *openEntity;
+    if (entityList == NULL) {
+      if (freeInternalEntities == NULL)
+        break;
+      entityList = freeInternalEntities;
+      freeInternalEntities = NULL;
+    }
+    openEntity = entityList;
+    entityList = entityList->next;
+    FREE(openEntity);
+  }
+
+  destroyBindings(freeBindingList, parser);
+  destroyBindings(inheritedBindings, parser);
+  poolDestroy(&tempPool);
+  poolDestroy(&temp2Pool);
+#ifdef XML_DTD
+  /* external parameter entity parsers share the DTD structure
+     parser->m_dtd with the root parser, so we must not destroy it
+  */
+  if (!isParamEntity && _dtd)
+#else
+  if (_dtd)
+#endif /* XML_DTD */
+    dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem);
+  FREE((void *)atts);
+#ifdef XML_ATTR_INFO
+  FREE((void *)attInfo);
+#endif
+  FREE(groupConnector);
+  FREE(buffer);
+  FREE(dataBuf);
+  FREE(nsAtts);
+  FREE(unknownEncodingMem);
+  if (unknownEncodingRelease)
+    unknownEncodingRelease(unknownEncodingData);
+  FREE(parser);
+}
+
+void XMLCALL
+XML_UseParserAsHandlerArg(XML_Parser parser)
+{
+  handlerArg = parser;
+}
+
+enum XML_Error XMLCALL
+XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD)
+{
+#ifdef XML_DTD
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+    return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING;
+  useForeignDTD = useDTD;
+  return XML_ERROR_NONE;
+#else
+  return XML_ERROR_FEATURE_REQUIRES_XML_DTD;
+#endif
+}
+
+void XMLCALL
+XML_SetReturnNSTriplet(XML_Parser parser, int do_nst)
+{
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+    return;
+  ns_triplets = do_nst ? XML_TRUE : XML_FALSE;
+}
+
+void XMLCALL
+XML_SetUserData(XML_Parser parser, void *p)
+{
+  if (handlerArg == userData)
+    handlerArg = userData = p;
+  else
+    userData = p;
+}
+
+enum XML_Status XMLCALL
+XML_SetBase(XML_Parser parser, const XML_Char *p)
+{
+  if (p) {
+    p = poolCopyString(&_dtd->pool, p);
+    if (!p)
+      return XML_STATUS_ERROR;
+    curBase = p;
+  }
+  else
+    curBase = NULL;
+  return XML_STATUS_OK;
+}
+
+const XML_Char * XMLCALL
+XML_GetBase(XML_Parser parser)
+{
+  return curBase;
+}
+
+int XMLCALL
+XML_GetSpecifiedAttributeCount(XML_Parser parser)
+{
+  return nSpecifiedAtts;
+}
+
+int XMLCALL
+XML_GetIdAttributeIndex(XML_Parser parser)
+{
+  return idAttIndex;
+}
+
+#ifdef XML_ATTR_INFO
+const XML_AttrInfo * XMLCALL
+XML_GetAttributeInfo(XML_Parser parser)
+{
+  return attInfo;
+}
+#endif
+
+void XMLCALL
+XML_SetElementHandler(XML_Parser parser,
+                      XML_StartElementHandler start,
+                      XML_EndElementHandler end)
+{
+  startElementHandler = start;
+  endElementHandler = end;
+}
+
+void XMLCALL
+XML_SetStartElementHandler(XML_Parser parser,
+                           XML_StartElementHandler start) {
+  startElementHandler = start;
+}
+
+void XMLCALL
+XML_SetEndElementHandler(XML_Parser parser,
+                         XML_EndElementHandler end) {
+  endElementHandler = end;
+}
+
+void XMLCALL
+XML_SetCharacterDataHandler(XML_Parser parser,
+                            XML_CharacterDataHandler handler)
+{
+  characterDataHandler = handler;
+}
+
+void XMLCALL
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+                                    XML_ProcessingInstructionHandler handler)
+{
+  processingInstructionHandler = handler;
+}
+
+void XMLCALL
+XML_SetCommentHandler(XML_Parser parser,
+                      XML_CommentHandler handler)
+{
+  commentHandler = handler;
+}
+
+void XMLCALL
+XML_SetCdataSectionHandler(XML_Parser parser,
+                           XML_StartCdataSectionHandler start,
+                           XML_EndCdataSectionHandler end)
+{
+  startCdataSectionHandler = start;
+  endCdataSectionHandler = end;
+}
+
+void XMLCALL
+XML_SetStartCdataSectionHandler(XML_Parser parser,
+                                XML_StartCdataSectionHandler start) {
+  startCdataSectionHandler = start;
+}
+
+void XMLCALL
+XML_SetEndCdataSectionHandler(XML_Parser parser,
+                              XML_EndCdataSectionHandler end) {
+  endCdataSectionHandler = end;
+}
+
+void XMLCALL
+XML_SetDefaultHandler(XML_Parser parser,
+                      XML_DefaultHandler handler)
+{
+  defaultHandler = handler;
+  defaultExpandInternalEntities = XML_FALSE;
+}
+
+void XMLCALL
+XML_SetDefaultHandlerExpand(XML_Parser parser,
+                            XML_DefaultHandler handler)
+{
+  defaultHandler = handler;
+  defaultExpandInternalEntities = XML_TRUE;
+}
+
+void XMLCALL
+XML_SetDoctypeDeclHandler(XML_Parser parser,
+                          XML_StartDoctypeDeclHandler start,
+                          XML_EndDoctypeDeclHandler end)
+{
+  startDoctypeDeclHandler = start;
+  endDoctypeDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetStartDoctypeDeclHandler(XML_Parser parser,
+                               XML_StartDoctypeDeclHandler start) {
+  startDoctypeDeclHandler = start;
+}
+
+void XMLCALL
+XML_SetEndDoctypeDeclHandler(XML_Parser parser,
+                             XML_EndDoctypeDeclHandler end) {
+  endDoctypeDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+                                 XML_UnparsedEntityDeclHandler handler)
+{
+  unparsedEntityDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetNotationDeclHandler(XML_Parser parser,
+                           XML_NotationDeclHandler handler)
+{
+  notationDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+                            XML_StartNamespaceDeclHandler start,
+                            XML_EndNamespaceDeclHandler end)
+{
+  startNamespaceDeclHandler = start;
+  endNamespaceDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetStartNamespaceDeclHandler(XML_Parser parser,
+                                 XML_StartNamespaceDeclHandler start) {
+  startNamespaceDeclHandler = start;
+}
+
+void XMLCALL
+XML_SetEndNamespaceDeclHandler(XML_Parser parser,
+                               XML_EndNamespaceDeclHandler end) {
+  endNamespaceDeclHandler = end;
+}
+
+void XMLCALL
+XML_SetNotStandaloneHandler(XML_Parser parser,
+                            XML_NotStandaloneHandler handler)
+{
+  notStandaloneHandler = handler;
+}
+
+void XMLCALL
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+                                XML_ExternalEntityRefHandler handler)
+{
+  externalEntityRefHandler = handler;
+}
+
+void XMLCALL
+XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg)
+{
+  if (arg)
+    externalEntityRefHandlerArg = (XML_Parser)arg;
+  else
+    externalEntityRefHandlerArg = parser;
+}
+
+void XMLCALL
+XML_SetSkippedEntityHandler(XML_Parser parser,
+                            XML_SkippedEntityHandler handler)
+{
+  skippedEntityHandler = handler;
+}
+
+void XMLCALL
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+                              XML_UnknownEncodingHandler handler,
+                              void *data)
+{
+  unknownEncodingHandler = handler;
+  unknownEncodingHandlerData = data;
+}
+
+void XMLCALL
+XML_SetElementDeclHandler(XML_Parser parser,
+                          XML_ElementDeclHandler eldecl)
+{
+  elementDeclHandler = eldecl;
+}
+
+void XMLCALL
+XML_SetAttlistDeclHandler(XML_Parser parser,
+                          XML_AttlistDeclHandler attdecl)
+{
+  attlistDeclHandler = attdecl;
+}
+
+void XMLCALL
+XML_SetEntityDeclHandler(XML_Parser parser,
+                         XML_EntityDeclHandler handler)
+{
+  entityDeclHandler = handler;
+}
+
+void XMLCALL
+XML_SetXmlDeclHandler(XML_Parser parser,
+                      XML_XmlDeclHandler handler) {
+  xmlDeclHandler = handler;
+}
+
+int XMLCALL
+XML_SetParamEntityParsing(XML_Parser parser,
+                          enum XML_ParamEntityParsing peParsing)
+{
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+    return 0;
+#ifdef XML_DTD
+  paramEntityParsing = peParsing;
+  return 1;
+#else
+  return peParsing == XML_PARAM_ENTITY_PARSING_NEVER;
+#endif
+}
+
+int XMLCALL
+XML_SetHashSalt(XML_Parser parser,
+                unsigned long hash_salt)
+{
+  /* block after XML_Parse()/XML_ParseBuffer() has been called */
+  if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+    return 0;
+  hash_secret_salt = hash_salt;
+  return 1;
+}
+
+enum XML_Status XMLCALL
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
+{
+  switch (ps_parsing) {
+  case XML_SUSPENDED:
+    errorCode = XML_ERROR_SUSPENDED;
+    return XML_STATUS_ERROR;
+  case XML_FINISHED:
+    errorCode = XML_ERROR_FINISHED;
+    return XML_STATUS_ERROR;
+  case XML_INITIALIZED:
+    if (parentParser == NULL && !startParsing(parser)) {
+      errorCode = XML_ERROR_NO_MEMORY;
+      return XML_STATUS_ERROR;
+    }
+  default:
+    ps_parsing = XML_PARSING;
+  }
+
+  if (len == 0) {
+    ps_finalBuffer = (XML_Bool)isFinal;
+    if (!isFinal)
+      return XML_STATUS_OK;
+    positionPtr = bufferPtr;
+    parseEndPtr = bufferEnd;
+
+    /* If data are left over from last buffer, and we now know that these
+       data are the final chunk of input, then we have to check them again
+       to detect errors based on that fact.
+    */
+    errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr);
+
+    if (errorCode == XML_ERROR_NONE) {
+      switch (ps_parsing) {
+      case XML_SUSPENDED:
+        XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+        positionPtr = bufferPtr;
+        return XML_STATUS_SUSPENDED;
+      case XML_INITIALIZED:
+      case XML_PARSING:
+        ps_parsing = XML_FINISHED;
+        /* fall through */
+      default:
+        return XML_STATUS_OK;
+      }
+    }
+    eventEndPtr = eventPtr;
+    processor = errorProcessor;
+    return XML_STATUS_ERROR;
+  }
+#ifndef XML_CONTEXT_BYTES
+  else if (bufferPtr == bufferEnd) {
+    const char *end;
+    int nLeftOver;
+    enum XML_Status result;
+    parseEndByteIndex += len;
+    positionPtr = s;
+    ps_finalBuffer = (XML_Bool)isFinal;
+
+    errorCode = processor(parser, s, parseEndPtr = s + len, &end);
+
+    if (errorCode != XML_ERROR_NONE) {
+      eventEndPtr = eventPtr;
+      processor = errorProcessor;
+      return XML_STATUS_ERROR;
+    }
+    else {
+      switch (ps_parsing) {
+      case XML_SUSPENDED:
+        result = XML_STATUS_SUSPENDED;
+        break;
+      case XML_INITIALIZED:
+      case XML_PARSING:
+        if (isFinal) {
+          ps_parsing = XML_FINISHED;
+          return XML_STATUS_OK;
+        }
+      /* fall through */
+      default:
+        result = XML_STATUS_OK;
+      }
+    }
+
+    XmlUpdatePosition(encoding, positionPtr, end, &position);
+    nLeftOver = s + len - end;
+    if (nLeftOver) {
+      if (buffer == NULL || nLeftOver > bufferLim - buffer) {
+        /* FIXME avoid integer overflow */
+        char *temp;
+        temp = (buffer == NULL
+                ? (char *)MALLOC(len * 2)
+                : (char *)REALLOC(buffer, len * 2));
+        if (temp == NULL) {
+          errorCode = XML_ERROR_NO_MEMORY;
+          eventPtr = eventEndPtr = NULL;
+          processor = errorProcessor;
+          return XML_STATUS_ERROR;
+        }
+        buffer = temp;
+        bufferLim = buffer + len * 2;
+      }
+      memcpy(buffer, end, nLeftOver);
+    }
+    bufferPtr = buffer;
+    bufferEnd = buffer + nLeftOver;
+    positionPtr = bufferPtr;
+    parseEndPtr = bufferEnd;
+    eventPtr = bufferPtr;
+    eventEndPtr = bufferPtr;
+    return result;
+  }
+#endif  /* not defined XML_CONTEXT_BYTES */
+  else {
+    void *buff = XML_GetBuffer(parser, len);
+    if (buff == NULL)
+      return XML_STATUS_ERROR;
+    else {
+      memcpy(buff, s, len);
+      return XML_ParseBuffer(parser, len, isFinal);
+    }
+  }
+}
+
+enum XML_Status XMLCALL
+XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
+{
+  const char *start;
+  enum XML_Status result = XML_STATUS_OK;
+
+  switch (ps_parsing) {
+  case XML_SUSPENDED:
+    errorCode = XML_ERROR_SUSPENDED;
+    return XML_STATUS_ERROR;
+  case XML_FINISHED:
+    errorCode = XML_ERROR_FINISHED;
+    return XML_STATUS_ERROR;
+  case XML_INITIALIZED:
+    if (parentParser == NULL && !startParsing(parser)) {
+      errorCode = XML_ERROR_NO_MEMORY;
+      return XML_STATUS_ERROR;
+    }
+  default:
+    ps_parsing = XML_PARSING;
+  }
+
+  start = bufferPtr;
+  positionPtr = start;
+  bufferEnd += len;
+  parseEndPtr = bufferEnd;
+  parseEndByteIndex += len;
+  ps_finalBuffer = (XML_Bool)isFinal;
+
+  errorCode = processor(parser, start, parseEndPtr, &bufferPtr);
+
+  if (errorCode != XML_ERROR_NONE) {
+    eventEndPtr = eventPtr;
+    processor = errorProcessor;
+    return XML_STATUS_ERROR;
+  }
+  else {
+    switch (ps_parsing) {
+    case XML_SUSPENDED:
+      result = XML_STATUS_SUSPENDED;
+      break;
+    case XML_INITIALIZED:
+    case XML_PARSING:
+      if (isFinal) {
+        ps_parsing = XML_FINISHED;
+        return result;
+      }
+    default: ;  /* should not happen */
+    }
+  }
+
+  XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+  positionPtr = bufferPtr;
+  return result;
+}
+
+void * XMLCALL
+XML_GetBuffer(XML_Parser parser, int len)
+{
+  if (len < 0) {
+    errorCode = XML_ERROR_NO_MEMORY;
+    return NULL;
+  }
+  switch (ps_parsing) {
+  case XML_SUSPENDED:
+    errorCode = XML_ERROR_SUSPENDED;
+    return NULL;
+  case XML_FINISHED:
+    errorCode = XML_ERROR_FINISHED;
+    return NULL;
+  default: ;
+  }
+
+  if (len > bufferLim - bufferEnd) {
+#ifdef XML_CONTEXT_BYTES
+    int keep;
+#endif  /* defined XML_CONTEXT_BYTES */
+    /* Do not invoke signed arithmetic overflow: */
+    int neededSize = (int) ((unsigned)len + (unsigned)(bufferEnd - bufferPtr));
+    if (neededSize < 0) {
+      errorCode = XML_ERROR_NO_MEMORY;
+      return NULL;
+    }
+#ifdef XML_CONTEXT_BYTES
+    keep = (int)(bufferPtr - buffer);
+    if (keep > XML_CONTEXT_BYTES)
+      keep = XML_CONTEXT_BYTES;
+    neededSize += keep;
+#endif  /* defined XML_CONTEXT_BYTES */
+    if (neededSize  <= bufferLim - buffer) {
+#ifdef XML_CONTEXT_BYTES
+      if (keep < bufferPtr - buffer) {
+        int offset = (int)(bufferPtr - buffer) - keep;
+        memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep);
+        bufferEnd -= offset;
+        bufferPtr -= offset;
+      }
+#else
+      memmove(buffer, bufferPtr, bufferEnd - bufferPtr);
+      bufferEnd = buffer + (bufferEnd - bufferPtr);
+      bufferPtr = buffer;
+#endif  /* not defined XML_CONTEXT_BYTES */
+    }
+    else {
+      char *newBuf;
+      int bufferSize = (int)(bufferLim - bufferPtr);
+      if (bufferSize == 0)
+        bufferSize = INIT_BUFFER_SIZE;
+      do {
+        /* Do not invoke signed arithmetic overflow: */
+        bufferSize = (int) (2U * (unsigned) bufferSize);
+      } while (bufferSize < neededSize && bufferSize > 0);
+      if (bufferSize <= 0) {
+        errorCode = XML_ERROR_NO_MEMORY;
+        return NULL;
+      }
+      newBuf = (char *)MALLOC(bufferSize);
+      if (newBuf == 0) {
+        errorCode = XML_ERROR_NO_MEMORY;
+        return NULL;
+      }
+      bufferLim = newBuf + bufferSize;
+#ifdef XML_CONTEXT_BYTES
+      if (bufferPtr) {
+        int keep = (int)(bufferPtr - buffer);
+        if (keep > XML_CONTEXT_BYTES)
+          keep = XML_CONTEXT_BYTES;
+        memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep);
+        FREE(buffer);
+        buffer = newBuf;
+        bufferEnd = buffer + (bufferEnd - bufferPtr) + keep;
+        bufferPtr = buffer + keep;
+      }
+      else {
+        bufferEnd = newBuf + (bufferEnd - bufferPtr);
+        bufferPtr = buffer = newBuf;
+      }
+#else
+      if (bufferPtr) {
+        memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr);
+        FREE(buffer);
+      }
+      bufferEnd = newBuf + (bufferEnd - bufferPtr);
+      bufferPtr = buffer = newBuf;
+#endif  /* not defined XML_CONTEXT_BYTES */
+    }
+    eventPtr = eventEndPtr = NULL;
+    positionPtr = NULL;
+  }
+  return bufferEnd;
+}
+
+enum XML_Status XMLCALL
+XML_StopParser(XML_Parser parser, XML_Bool resumable)
+{
+  switch (ps_parsing) {
+  case XML_SUSPENDED:
+    if (resumable) {
+      errorCode = XML_ERROR_SUSPENDED;
+      return XML_STATUS_ERROR;
+    }
+    ps_parsing = XML_FINISHED;
+    break;
+  case XML_FINISHED:
+    errorCode = XML_ERROR_FINISHED;
+    return XML_STATUS_ERROR;
+  default:
+    if (resumable) {
+#ifdef XML_DTD
+      if (isParamEntity) {
+        errorCode = XML_ERROR_SUSPEND_PE;
+        return XML_STATUS_ERROR;
+      }
+#endif
+      ps_parsing = XML_SUSPENDED;
+    }
+    else
+      ps_parsing = XML_FINISHED;
+  }
+  return XML_STATUS_OK;
+}
+
+enum XML_Status XMLCALL
+XML_ResumeParser(XML_Parser parser)
+{
+  enum XML_Status result = XML_STATUS_OK;
+
+  if (ps_parsing != XML_SUSPENDED) {
+    errorCode = XML_ERROR_NOT_SUSPENDED;
+    return XML_STATUS_ERROR;
+  }
+  ps_parsing = XML_PARSING;
+
+  errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr);
+
+  if (errorCode != XML_ERROR_NONE) {
+    eventEndPtr = eventPtr;
+    processor = errorProcessor;
+    return XML_STATUS_ERROR;
+  }
+  else {
+    switch (ps_parsing) {
+    case XML_SUSPENDED:
+      result = XML_STATUS_SUSPENDED;
+      break;
+    case XML_INITIALIZED:
+    case XML_PARSING:
+      if (ps_finalBuffer) {
+        ps_parsing = XML_FINISHED;
+        return result;
+      }
+    default: ;
+    }
+  }
+
+  XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+  positionPtr = bufferPtr;
+  return result;
+}
+
+void XMLCALL
+XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status)
+{
+  assert(status != NULL);
+  *status = parser->m_parsingStatus;
+}
+
+enum XML_Error XMLCALL
+XML_GetErrorCode(XML_Parser parser)
+{
+  return errorCode;
+}
+
+XML_Index XMLCALL
+XML_GetCurrentByteIndex(XML_Parser parser)
+{
+  if (eventPtr)
+    return (XML_Index)(parseEndByteIndex - (parseEndPtr - eventPtr));
+  return -1;
+}
+
+int XMLCALL
+XML_GetCurrentByteCount(XML_Parser parser)
+{
+  if (eventEndPtr && eventPtr)
+    return (int)(eventEndPtr - eventPtr);
+  return 0;
+}
+
+const char * XMLCALL
+XML_GetInputContext(XML_Parser parser, int *offset, int *size)
+{
+#ifdef XML_CONTEXT_BYTES
+  if (eventPtr && buffer) {
+    *offset = (int)(eventPtr - buffer);
+    *size   = (int)(bufferEnd - buffer);
+    return buffer;
+  }
+#endif /* defined XML_CONTEXT_BYTES */
+  return (char *) 0;
+}
+
+XML_Size XMLCALL
+XML_GetCurrentLineNumber(XML_Parser parser)
+{
+  if (eventPtr && eventPtr >= positionPtr) {
+    XmlUpdatePosition(encoding, positionPtr, eventPtr, &position);
+    positionPtr = eventPtr;
+  }
+  return position.lineNumber + 1;
+}
+
+XML_Size XMLCALL
+XML_GetCurrentColumnNumber(XML_Parser parser)
+{
+  if (eventPtr && eventPtr >= positionPtr) {
+    XmlUpdatePosition(encoding, positionPtr, eventPtr, &position);
+    positionPtr = eventPtr;
+  }
+  return position.columnNumber;
+}
+
+void XMLCALL
+XML_FreeContentModel(XML_Parser parser, XML_Content *model)
+{
+  FREE(model);
+}
+
+void * XMLCALL
+XML_MemMalloc(XML_Parser parser, size_t size)
+{
+  return MALLOC(size);
+}
+
+void * XMLCALL
+XML_MemRealloc(XML_Parser parser, void *ptr, size_t size)
+{
+  return REALLOC(ptr, size);
+}
+
+void XMLCALL
+XML_MemFree(XML_Parser parser, void *ptr)
+{
+  FREE(ptr);
+}
+
+void XMLCALL
+XML_DefaultCurrent(XML_Parser parser)
+{
+  if (defaultHandler) {
+    if (openInternalEntities)
+      reportDefault(parser,
+                    internalEncoding,
+                    openInternalEntities->internalEventPtr,
+                    openInternalEntities->internalEventEndPtr);
+    else
+      reportDefault(parser, encoding, eventPtr, eventEndPtr);
+  }
+}
+
+const XML_LChar * XMLCALL
+XML_ErrorString(enum XML_Error code)
+{
+  static const XML_LChar* const message[] = {
+    0,
+    XML_L("out of memory"),
+    XML_L("syntax error"),
+    XML_L("no element found"),
+    XML_L("not well-formed (invalid token)"),
+    XML_L("unclosed token"),
+    XML_L("partial character"),
+    XML_L("mismatched tag"),
+    XML_L("duplicate attribute"),
+    XML_L("junk after document element"),
+    XML_L("illegal parameter entity reference"),
+    XML_L("undefined entity"),
+    XML_L("recursive entity reference"),
+    XML_L("asynchronous entity"),
+    XML_L("reference to invalid character number"),
+    XML_L("reference to binary entity"),
+    XML_L("reference to external entity in attribute"),
+    XML_L("XML or text declaration not at start of entity"),
+    XML_L("unknown encoding"),
+    XML_L("encoding specified in XML declaration is incorrect"),
+    XML_L("unclosed CDATA section"),
+    XML_L("error in processing external entity reference"),
+    XML_L("document is not standalone"),
+    XML_L("unexpected parser state - please send a bug report"),
+    XML_L("entity declared in parameter entity"),
+    XML_L("requested feature requires XML_DTD support in Expat"),
+    XML_L("cannot change setting once parsing has begun"),
+    XML_L("unbound prefix"),
+    XML_L("must not undeclare prefix"),
+    XML_L("incomplete markup in parameter entity"),
+    XML_L("XML declaration not well-formed"),
+    XML_L("text declaration not well-formed"),
+    XML_L("illegal character(s) in public id"),
+    XML_L("parser suspended"),
+    XML_L("parser not suspended"),
+    XML_L("parsing aborted"),
+    XML_L("parsing finished"),
+    XML_L("cannot suspend in external parameter entity"),
+    XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"),
+    XML_L("reserved prefix (xmlns) must not be declared or undeclared"),
+    XML_L("prefix must not be bound to one of the reserved namespace names")
+  };
+  if (code > 0 && code < sizeof(message)/sizeof(message[0]))
+    return message[code];
+  return NULL;
+}
+
+const XML_LChar * XMLCALL
+XML_ExpatVersion(void) {
+
+  /* V1 is used to string-ize the version number. However, it would
+     string-ize the actual version macro *names* unless we get them
+     substituted before being passed to V1. CPP is defined to expand
+     a macro, then rescan for more expansions. Thus, we use V2 to expand
+     the version macros, then CPP will expand the resulting V1() macro
+     with the correct numerals. */
+  /* ### I'm assuming cpp is portable in this respect... */
+
+#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c)
+#define V2(a,b,c) XML_L("expat_")V1(a,b,c)
+
+  return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION);
+
+#undef V1
+#undef V2
+}
+
+XML_Expat_Version XMLCALL
+XML_ExpatVersionInfo(void)
+{
+  XML_Expat_Version version;
+
+  version.major = XML_MAJOR_VERSION;
+  version.minor = XML_MINOR_VERSION;
+  version.micro = XML_MICRO_VERSION;
+
+  return version;
+}
+
+const XML_Feature * XMLCALL
+XML_GetFeatureList(void)
+{
+  static const XML_Feature features[] = {
+    {XML_FEATURE_SIZEOF_XML_CHAR,  XML_L("sizeof(XML_Char)"),
+     sizeof(XML_Char)},
+    {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
+     sizeof(XML_LChar)},
+#ifdef XML_UNICODE
+    {XML_FEATURE_UNICODE,          XML_L("XML_UNICODE"), 0},
+#endif
+#ifdef XML_UNICODE_WCHAR_T
+    {XML_FEATURE_UNICODE_WCHAR_T,  XML_L("XML_UNICODE_WCHAR_T"), 0},
+#endif
+#ifdef XML_DTD
+    {XML_FEATURE_DTD,              XML_L("XML_DTD"), 0},
+#endif
+#ifdef XML_CONTEXT_BYTES
+    {XML_FEATURE_CONTEXT_BYTES,    XML_L("XML_CONTEXT_BYTES"),
+     XML_CONTEXT_BYTES},
+#endif
+#ifdef XML_MIN_SIZE
+    {XML_FEATURE_MIN_SIZE,         XML_L("XML_MIN_SIZE"), 0},
+#endif
+#ifdef XML_NS
+    {XML_FEATURE_NS,               XML_L("XML_NS"), 0},
+#endif
+#ifdef XML_LARGE_SIZE
+    {XML_FEATURE_LARGE_SIZE,       XML_L("XML_LARGE_SIZE"), 0},
+#endif
+#ifdef XML_ATTR_INFO
+    {XML_FEATURE_ATTR_INFO,        XML_L("XML_ATTR_INFO"), 0},
+#endif
+    {XML_FEATURE_END,              NULL, 0}
+  };
+
+  return features;
+}
+
+/* Initially tag->rawName always points into the parse buffer;
+   for those TAG instances opened while the current parse buffer was
+   processed, and not yet closed, we need to store tag->rawName in a more
+   permanent location, since the parse buffer is about to be discarded.
+*/
+static XML_Bool
+storeRawNames(XML_Parser parser)
+{
+  TAG *tag = tagStack;
+  while (tag) {
+    int bufSize;
+    int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
+    char *rawNameBuf = tag->buf + nameLen;
+    /* Stop if already stored.  Since tagStack is a stack, we can stop
+       at the first entry that has already been copied; everything
+       below it in the stack is already been accounted for in a
+       previous call to this function.
+    */
+    if (tag->rawName == rawNameBuf)
+      break;
+    /* For re-use purposes we need to ensure that the
+       size of tag->buf is a multiple of sizeof(XML_Char).
+    */
+    bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+    if (bufSize > tag->bufEnd - tag->buf) {
+      char *temp = (char *)REALLOC(tag->buf, bufSize);
+      if (temp == NULL)
+        return XML_FALSE;
+      /* if tag->name.str points to tag->buf (only when namespace
+         processing is off) then we have to update it
+      */
+      if (tag->name.str == (XML_Char *)tag->buf)
+        tag->name.str = (XML_Char *)temp;
+      /* if tag->name.localPart is set (when namespace processing is on)
+         then update it as well, since it will always point into tag->buf
+      */
+      if (tag->name.localPart)
+        tag->name.localPart = (XML_Char *)temp + (tag->name.localPart -
+                                                  (XML_Char *)tag->buf);
+      tag->buf = temp;
+      tag->bufEnd = temp + bufSize;
+      rawNameBuf = temp + nameLen;
+    }
+    memcpy(rawNameBuf, tag->rawName, tag->rawNameLength);
+    tag->rawName = rawNameBuf;
+    tag = tag->parent;
+  }
+  return XML_TRUE;
+}
+
+static enum XML_Error PTRCALL
+contentProcessor(XML_Parser parser,
+                 const char *start,
+                 const char *end,
+                 const char **endPtr)
+{
+  enum XML_Error result = doContent(parser, 0, encoding, start, end,
+                                    endPtr, (XML_Bool)!ps_finalBuffer);
+  if (result == XML_ERROR_NONE) {
+    if (!storeRawNames(parser))
+      return XML_ERROR_NO_MEMORY;
+  }
+  return result;
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor(XML_Parser parser,
+                            const char *start,
+                            const char *end,
+                            const char **endPtr)
+{
+  enum XML_Error result = initializeEncoding(parser);
+  if (result != XML_ERROR_NONE)
+    return result;
+  processor = externalEntityInitProcessor2;
+  return externalEntityInitProcessor2(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor2(XML_Parser parser,
+                             const char *start,
+                             const char *end,
+                             const char **endPtr)
+{
+  const char *next = start; /* XmlContentTok doesn't always set the last arg */
+  int tok = XmlContentTok(encoding, start, end, &next);
+  switch (tok) {
+  case XML_TOK_BOM:
+    /* If we are at the end of the buffer, this would cause the next stage,
+       i.e. externalEntityInitProcessor3, to pass control directly to
+       doContent (by detecting XML_TOK_NONE) without processing any xml text
+       declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent.
+    */
+    if (next == end && !ps_finalBuffer) {
+      *endPtr = next;
+      return XML_ERROR_NONE;
+    }
+    start = next;
+    break;
+  case XML_TOK_PARTIAL:
+    if (!ps_finalBuffer) {
+      *endPtr = start;
+      return XML_ERROR_NONE;
+    }
+    eventPtr = start;
+    return XML_ERROR_UNCLOSED_TOKEN;
+  case XML_TOK_PARTIAL_CHAR:
+    if (!ps_finalBuffer) {
+      *endPtr = start;
+      return XML_ERROR_NONE;
+    }
+    eventPtr = start;
+    return XML_ERROR_PARTIAL_CHAR;
+  }
+  processor = externalEntityInitProcessor3;
+  return externalEntityInitProcessor3(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityInitProcessor3(XML_Parser parser,
+                             const char *start,
+                             const char *end,
+                             const char **endPtr)
+{
+  int tok;
+  const char *next = start; /* XmlContentTok doesn't always set the last arg */
+  eventPtr = start;
+  tok = XmlContentTok(encoding, start, end, &next);
+  eventEndPtr = next;
+
+  switch (tok) {
+  case XML_TOK_XML_DECL:
+    {
+      enum XML_Error result;
+      result = processXmlDecl(parser, 1, start, next);
+      if (result != XML_ERROR_NONE)
+        return result;
+      switch (ps_parsing) {
+      case XML_SUSPENDED:
+        *endPtr = next;
+        return XML_ERROR_NONE;
+      case XML_FINISHED:
+        return XML_ERROR_ABORTED;
+      default:
+        start = next;
+      }
+    }
+    break;
+  case XML_TOK_PARTIAL:
+    if (!ps_finalBuffer) {
+      *endPtr = start;
+      return XML_ERROR_NONE;
+    }
+    return XML_ERROR_UNCLOSED_TOKEN;
+  case XML_TOK_PARTIAL_CHAR:
+    if (!ps_finalBuffer) {
+      *endPtr = start;
+      return XML_ERROR_NONE;
+    }
+    return XML_ERROR_PARTIAL_CHAR;
+  }
+  processor = externalEntityContentProcessor;
+  tagLevel = 1;
+  return externalEntityContentProcessor(parser, start, end, endPtr);
+}
+
+static enum XML_Error PTRCALL
+externalEntityContentProcessor(XML_Parser parser,
+                               const char *start,
+                               const char *end,
+                               const char **endPtr)
+{
+  enum XML_Error result = doContent(parser, 1, encoding, start, end,
+                                    endPtr, (XML_Bool)!ps_finalBuffer);
+  if (result == XML_ERROR_NONE) {
+    if (!storeRawNames(parser))
+      return XML_ERROR_NO_MEMORY;
+  }
+  return result;
+}
+
+static enum XML_Error
+doContent(XML_Parser parser,
+          int startTagLevel,
+          const ENCODING *enc,
+          const char *s,
+          const char *end,
+          const char **nextPtr,
+          XML_Bool haveMore)
+{
+  /* save one level of indirection */
+  DTD * const dtd = _dtd;
+
+  const char **eventPP;
+  const char **eventEndPP;
+  if (enc == encoding) {
+    eventPP = &eventPtr;
+    eventEndPP = &eventEndPtr;
+  }
+  else {
+    eventPP = &(openInternalEntities->internalEventPtr);
+    eventEndPP = &(openInternalEntities->internalEventEndPtr);
+  }
+  *eventPP = s;
+
+  for (;;) {
+    const char *next = s; /* XmlContentTok doesn't always set the last arg */
+    int tok = XmlContentTok(enc, s, end, &next);
+    *eventEndPP = next;
+    switch (tok) {
+    case XML_TOK_TRAILING_CR:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      *eventEndPP = end;
+      if (characterDataHandler) {
+        XML_Char c = 0xA;
+        characterDataHandler(handlerArg, &c, 1);
+      }
+      else if (defaultHandler)
+        reportDefault(parser, enc, s, end);
+      /* We are at the end of the final buffer, should we check for
+         XML_SUSPENDED, XML_FINISHED?
+      */
+      if (startTagLevel == 0)
+        return XML_ERROR_NO_ELEMENTS;
+      if (tagLevel != startTagLevel)
+        return XML_ERROR_ASYNC_ENTITY;
+      *nextPtr = end;
+      return XML_ERROR_NONE;
+    case XML_TOK_NONE:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      if (startTagLevel > 0) {
+        if (tagLevel != startTagLevel)
+          return XML_ERROR_ASYNC_ENTITY;
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_NO_ELEMENTS;
+    case XML_TOK_INVALID:
+      *eventPP = next;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_UNCLOSED_TOKEN;
+    case XML_TOK_PARTIAL_CHAR:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_PARTIAL_CHAR;
+    case XML_TOK_ENTITY_REF:
+      {
+        const XML_Char *name;
+        ENTITY *entity;
+        XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc,
+                                              s + enc->minBytesPerChar,
+                                              next - enc->minBytesPerChar);
+        if (ch) {
+          if (characterDataHandler)
+            characterDataHandler(handlerArg, &ch, 1);
+          else if (defaultHandler)
+            reportDefault(parser, enc, s, next);
+          break;
+        }
+        name = poolStoreString(&dtd->pool, enc,
+                                s + enc->minBytesPerChar,
+                                next - enc->minBytesPerChar);
+        if (!name)
+          return XML_ERROR_NO_MEMORY;
+        entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0);
+        poolDiscard(&dtd->pool);
+        /* First, determine if a check for an existing declaration is needed;
+           if yes, check that the entity exists, and that it is internal,
+           otherwise call the skipped entity or default handler.
+        */
+        if (!dtd->hasParamEntityRefs || dtd->standalone) {
+          if (!entity)
+            return XML_ERROR_UNDEFINED_ENTITY;
+          else if (!entity->is_internal)
+            return XML_ERROR_ENTITY_DECLARED_IN_PE;
+        }
+        else if (!entity) {
+          if (skippedEntityHandler)
+            skippedEntityHandler(handlerArg, name, 0);
+          else if (defaultHandler)
+            reportDefault(parser, enc, s, next);
+          break;
+        }
+        if (entity->open)
+          return XML_ERROR_RECURSIVE_ENTITY_REF;
+        if (entity->notation)
+          return XML_ERROR_BINARY_ENTITY_REF;
+        if (entity->textPtr) {
+          enum XML_Error result;
+          if (!defaultExpandInternalEntities) {
+            if (skippedEntityHandler)
+              skippedEntityHandler(handlerArg, entity->name, 0);
+            else if (defaultHandler)
+              reportDefault(parser, enc, s, next);
+            break;
+          }
+          result = processInternalEntity(parser, entity, XML_FALSE);
+          if (result != XML_ERROR_NONE)
+            return result;
+        }
+        else if (externalEntityRefHandler) {
+          const XML_Char *context;
+          entity->open = XML_TRUE;
+          context = getContext(parser);
+          entity->open = XML_FALSE;
+          if (!context)
+            return XML_ERROR_NO_MEMORY;
+          if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+                                        context,
+                                        entity->base,
+                                        entity->systemId,
+                                        entity->publicId))
+            return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+          poolDiscard(&tempPool);
+        }
+        else if (defaultHandler)
+          reportDefault(parser, enc, s, next);
+        break;
+      }
+    case XML_TOK_START_TAG_NO_ATTS:
+      /* fall through */
+    case XML_TOK_START_TAG_WITH_ATTS:
+      {
+        TAG *tag;
+        enum XML_Error result;
+        XML_Char *toPtr;
+        if (freeTagList) {
+          tag = freeTagList;
+          freeTagList = freeTagList->parent;
+        }
+        else {
+          tag = (TAG *)MALLOC(sizeof(TAG));
+          if (!tag)
+            return XML_ERROR_NO_MEMORY;
+          tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE);
+          if (!tag->buf) {
+            FREE(tag);
+            return XML_ERROR_NO_MEMORY;
+          }
+          tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE;
+        }
+        tag->bindings = NULL;
+        tag->parent = tagStack;
+        tagStack = tag;
+        tag->name.localPart = NULL;
+        tag->name.prefix = NULL;
+        tag->rawName = s + enc->minBytesPerChar;
+        tag->rawNameLength = XmlNameLength(enc, tag->rawName);
+        ++tagLevel;
+        {
+          const char *rawNameEnd = tag->rawName + tag->rawNameLength;
+          const char *fromPtr = tag->rawName;
+          toPtr = (XML_Char *)tag->buf;
+          for (;;) {
+            int bufSize;
+            int convLen;
+            const enum XML_Convert_Result convert_res = XmlConvert(enc,
+                       &fromPtr, rawNameEnd,
+                       (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1);
+            convLen = (int)(toPtr - (XML_Char *)tag->buf);
+            if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) {
+              tag->name.strLen = convLen;
+              break;
+            }
+            bufSize = (int)(tag->bufEnd - tag->buf) << 1;
+            {
+              char *temp = (char *)REALLOC(tag->buf, bufSize);
+              if (temp == NULL)
+                return XML_ERROR_NO_MEMORY;
+              tag->buf = temp;
+              tag->bufEnd = temp + bufSize;
+              toPtr = (XML_Char *)temp + convLen;
+            }
+          }
+        }
+        tag->name.str = (XML_Char *)tag->buf;
+        *toPtr = XML_T('\0');
+        result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
+        if (result)
+          return result;
+        if (startElementHandler)
+          startElementHandler(handlerArg, tag->name.str,
+                              (const XML_Char **)atts);
+        else if (defaultHandler)
+          reportDefault(parser, enc, s, next);
+        poolClear(&tempPool);
+        break;
+      }
+    case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
+      /* fall through */
+    case XML_TOK_EMPTY_ELEMENT_WITH_ATTS:
+      {
+        const char *rawName = s + enc->minBytesPerChar;
+        enum XML_Error result;
+        BINDING *bindings = NULL;
+        XML_Bool noElmHandlers = XML_TRUE;
+        TAG_NAME name;
+        name.str = poolStoreString(&tempPool, enc, rawName,
+                                   rawName + XmlNameLength(enc, rawName));
+        if (!name.str)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&tempPool);
+        result = storeAtts(parser, enc, s, &name, &bindings);
+        if (result)
+          return result;
+        poolFinish(&tempPool);
+        if (startElementHandler) {
+          startElementHandler(handlerArg, name.str, (const XML_Char **)atts);
+          noElmHandlers = XML_FALSE;
+        }
+        if (endElementHandler) {
+          if (startElementHandler)
+            *eventPP = *eventEndPP;
+          endElementHandler(handlerArg, name.str);
+          noElmHandlers = XML_FALSE;
+        }
+        if (noElmHandlers && defaultHandler)
+          reportDefault(parser, enc, s, next);
+        poolClear(&tempPool);
+        while (bindings) {
+          BINDING *b = bindings;
+          if (endNamespaceDeclHandler)
+            endNamespaceDeclHandler(handlerArg, b->prefix->name);
+          bindings = bindings->nextTagBinding;
+          b->nextTagBinding = freeBindingList;
+          freeBindingList = b;
+          b->prefix->binding = b->prevPrefixBinding;
+        }
+      }
+      if (tagLevel == 0)
+        return epilogProcessor(parser, next, end, nextPtr);
+      break;
+    case XML_TOK_END_TAG:
+      if (tagLevel == startTagLevel)
+        return XML_ERROR_ASYNC_ENTITY;
+      else {
+        int len;
+        const char *rawName;
+        TAG *tag = tagStack;
+        tagStack = tag->parent;
+        tag->parent = freeTagList;
+        freeTagList = tag;
+        rawName = s + enc->minBytesPerChar*2;
+        len = XmlNameLength(enc, rawName);
+        if (len != tag->rawNameLength
+            || memcmp(tag->rawName, rawName, len) != 0) {
+          *eventPP = rawName;
+          return XML_ERROR_TAG_MISMATCH;
+        }
+        --tagLevel;
+        if (endElementHandler) {
+          const XML_Char *localPart;
+          const XML_Char *prefix;
+          XML_Char *uri;
+          localPart = tag->name.localPart;
+          if (ns && localPart) {
+            /* localPart and prefix may have been overwritten in
+               tag->name.str, since this points to the binding->uri
+               buffer which gets re-used; so we have to add them again
+            */
+            uri = (XML_Char *)tag->name.str + tag->name.uriLen;
+            /* don't need to check for space - already done in storeAtts() */
+            while (*localPart) *uri++ = *localPart++;
+            prefix = (XML_Char *)tag->name.prefix;
+            if (ns_triplets && prefix) {
+              *uri++ = namespaceSeparator;
+              while (*prefix) *uri++ = *prefix++;
+             }
+            *uri = XML_T('\0');
+          }
+          endElementHandler(handlerArg, tag->name.str);
+        }
+        else if (defaultHandler)
+          reportDefault(parser, enc, s, next);
+        while (tag->bindings) {
+          BINDING *b = tag->bindings;
+          if (endNamespaceDeclHandler)
+            endNamespaceDeclHandler(handlerArg, b->prefix->name);
+          tag->bindings = tag->bindings->nextTagBinding;
+          b->nextTagBinding = freeBindingList;
+          freeBindingList = b;
+          b->prefix->binding = b->prevPrefixBinding;
+        }
+        if (tagLevel == 0)
+          return epilogProcessor(parser, next, end, nextPtr);
+      }
+      break;
+    case XML_TOK_CHAR_REF:
+      {
+        int n = XmlCharRefNumber(enc, s);
+        if (n < 0)
+          return XML_ERROR_BAD_CHAR_REF;
+        if (characterDataHandler) {
+          XML_Char buf[XML_ENCODE_MAX];
+          characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf));
+        }
+        else if (defaultHandler)
+          reportDefault(parser, enc, s, next);
+      }
+      break;
+    case XML_TOK_XML_DECL:
+      return XML_ERROR_MISPLACED_XML_PI;
+    case XML_TOK_DATA_NEWLINE:
+      if (characterDataHandler) {
+        XML_Char c = 0xA;
+        characterDataHandler(handlerArg, &c, 1);
+      }
+      else if (defaultHandler)
+        reportDefault(parser, enc, s, next);
+      break;
+    case XML_TOK_CDATA_SECT_OPEN:
+      {
+        enum XML_Error result;
+        if (startCdataSectionHandler)
+          startCdataSectionHandler(handlerArg);
+#if 0
+        /* Suppose you doing a transformation on a document that involves
+           changing only the character data.  You set up a defaultHandler
+           and a characterDataHandler.  The defaultHandler simply copies
+           characters through.  The characterDataHandler does the
+           transformation and writes the characters out escaping them as
+           necessary.  This case will fail to work if we leave out the
+           following two lines (because & and < inside CDATA sections will
+           be incorrectly escaped).
+
+           However, now we have a start/endCdataSectionHandler, so it seems
+           easier to let the user deal with this.
+        */
+        else if (characterDataHandler)
+          characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+        else if (defaultHandler)
+          reportDefault(parser, enc, s, next);
+        result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore);
+        if (result != XML_ERROR_NONE)
+          return result;
+        else if (!next) {
+          processor = cdataSectionProcessor;
+          return result;
+        }
+      }
+      break;
+    case XML_TOK_TRAILING_RSQB:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      if (characterDataHandler) {
+        if (MUST_CONVERT(enc, s)) {
+          ICHAR *dataPtr = (ICHAR *)dataBuf;
+          XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+          characterDataHandler(handlerArg, dataBuf,
+                               (int)(dataPtr - (ICHAR *)dataBuf));
+        }
+        else
+          characterDataHandler(handlerArg,
+                               (XML_Char *)s,
+                               (int)((XML_Char *)end - (XML_Char *)s));
+      }
+      else if (defaultHandler)
+        reportDefault(parser, enc, s, end);
+      /* We are at the end of the final buffer, should we check for
+         XML_SUSPENDED, XML_FINISHED?
+      */
+      if (startTagLevel == 0) {
+        *eventPP = end;
+        return XML_ERROR_NO_ELEMENTS;
+      }
+      if (tagLevel != startTagLevel) {
+        *eventPP = end;
+        return XML_ERROR_ASYNC_ENTITY;
+      }
+      *nextPtr = end;
+      return XML_ERROR_NONE;
+    case XML_TOK_DATA_CHARS:
+      {
+        XML_CharacterDataHandler charDataHandler = characterDataHandler;
+        if (charDataHandler) {
+          if (MUST_CONVERT(enc, s)) {
+            for (;;) {
+              ICHAR *dataPtr = (ICHAR *)dataBuf;
+              const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+              *eventEndPP = s;
+              charDataHandler(handlerArg, dataBuf,
+                              (int)(dataPtr - (ICHAR *)dataBuf));
+              if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
+                break;
+              *eventPP = s;
+            }
+          }
+          else
+            charDataHandler(handlerArg,
+                            (XML_Char *)s,
+                            (int)((XML_Char *)next - (XML_Char *)s));
+        }
+        else if (defaultHandler)
+          reportDefault(parser, enc, s, next);
+      }
+      break;
+    case XML_TOK_PI:
+      if (!reportProcessingInstruction(parser, enc, s, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_COMMENT:
+      if (!reportComment(parser, enc, s, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    default:
+      if (defaultHandler)
+        reportDefault(parser, enc, s, next);
+      break;
+    }
+    *eventPP = s = next;
+    switch (ps_parsing) {
+    case XML_SUSPENDED:
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default: ;
+    }
+  }
+  /* not reached */
+}
+
+/* Precondition: all arguments must be non-NULL;
+   Purpose:
+   - normalize attributes
+   - check attributes for well-formedness
+   - generate namespace aware attribute names (URI, prefix)
+   - build list of attributes for startElementHandler
+   - default attributes
+   - process namespace declarations (check and report them)
+   - generate namespace aware element name (URI, prefix)
+*/
+static enum XML_Error
+storeAtts(XML_Parser parser, const ENCODING *enc,
+          const char *attStr, TAG_NAME *tagNamePtr,
+          BINDING **bindingsPtr)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  ELEMENT_TYPE *elementType;
+  int nDefaultAtts;
+  const XML_Char **appAtts;   /* the attribute list for the application */
+  int attIndex = 0;
+  int prefixLen;
+  int i;
+  int n;
+  XML_Char *uri;
+  int nPrefixes = 0;
+  BINDING *binding;
+  const XML_Char *localPart;
+
+  /* lookup the element type name */
+  elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str,0);
+  if (!elementType) {
+    const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str);
+    if (!name)
+      return XML_ERROR_NO_MEMORY;
+    elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name,
+                                         sizeof(ELEMENT_TYPE));
+    if (!elementType)
+      return XML_ERROR_NO_MEMORY;
+    if (ns && !setElementTypePrefix(parser, elementType))
+      return XML_ERROR_NO_MEMORY;
+  }
+  nDefaultAtts = elementType->nDefaultAtts;
+
+  /* get the attributes from the tokenizer */
+  n = XmlGetAttributes(enc, attStr, attsSize, atts);
+  if (n + nDefaultAtts > attsSize) {
+    int oldAttsSize = attsSize;
+    ATTRIBUTE *temp;
+#ifdef XML_ATTR_INFO
+    XML_AttrInfo *temp2;
+#endif
+    attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+    temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE));
+    if (temp == NULL)
+      return XML_ERROR_NO_MEMORY;
+    atts = temp;
+#ifdef XML_ATTR_INFO
+    temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo));
+    if (temp2 == NULL)
+      return XML_ERROR_NO_MEMORY;
+    attInfo = temp2;
+#endif
+    if (n > oldAttsSize)
+      XmlGetAttributes(enc, attStr, n, atts);
+  }
+
+  appAtts = (const XML_Char **)atts;
+  for (i = 0; i < n; i++) {
+    ATTRIBUTE *currAtt = &atts[i];
+#ifdef XML_ATTR_INFO
+    XML_AttrInfo *currAttInfo = &attInfo[i];
+#endif
+    /* add the name and value to the attribute list */
+    ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name,
+                                         currAtt->name
+                                         + XmlNameLength(enc, currAtt->name));
+    if (!attId)
+      return XML_ERROR_NO_MEMORY;
+#ifdef XML_ATTR_INFO
+    currAttInfo->nameStart = parseEndByteIndex - (parseEndPtr - currAtt->name);
+    currAttInfo->nameEnd = currAttInfo->nameStart +
+                           XmlNameLength(enc, currAtt->name);
+    currAttInfo->valueStart = parseEndByteIndex -
+                            (parseEndPtr - currAtt->valuePtr);
+    currAttInfo->valueEnd = parseEndByteIndex - (parseEndPtr - currAtt->valueEnd);
+#endif
+    /* Detect duplicate attributes by their QNames. This does not work when
+       namespace processing is turned on and different prefixes for the same
+       namespace are used. For this case we have a check further down.
+    */
+    if ((attId->name)[-1]) {
+      if (enc == encoding)
+        eventPtr = atts[i].name;
+      return XML_ERROR_DUPLICATE_ATTRIBUTE;
+    }
+    (attId->name)[-1] = 1;
+    appAtts[attIndex++] = attId->name;
+    if (!atts[i].normalized) {
+      enum XML_Error result;
+      XML_Bool isCdata = XML_TRUE;
+
+      /* figure out whether declared as other than CDATA */
+      if (attId->maybeTokenized) {
+        int j;
+        for (j = 0; j < nDefaultAtts; j++) {
+          if (attId == elementType->defaultAtts[j].id) {
+            isCdata = elementType->defaultAtts[j].isCdata;
+            break;
+          }
+        }
+      }
+
+      /* normalize the attribute value */
+      result = storeAttributeValue(parser, enc, isCdata,
+                                   atts[i].valuePtr, atts[i].valueEnd,
+                                   &tempPool);
+      if (result)
+        return result;
+      appAtts[attIndex] = poolStart(&tempPool);
+      poolFinish(&tempPool);
+    }
+    else {
+      /* the value did not need normalizing */
+      appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr,
+                                          atts[i].valueEnd);
+      if (appAtts[attIndex] == 0)
+        return XML_ERROR_NO_MEMORY;
+      poolFinish(&tempPool);
+    }
+    /* handle prefixed attribute names */
+    if (attId->prefix) {
+      if (attId->xmlns) {
+        /* deal with namespace declarations here */
+        enum XML_Error result = addBinding(parser, attId->prefix, attId,
+                                           appAtts[attIndex], bindingsPtr);
+        if (result)
+          return result;
+        --attIndex;
+      }
+      else {
+        /* deal with other prefixed names later */
+        attIndex++;
+        nPrefixes++;
+        (attId->name)[-1] = 2;
+      }
+    }
+    else
+      attIndex++;
+  }
+
+  /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */
+  nSpecifiedAtts = attIndex;
+  if (elementType->idAtt && (elementType->idAtt->name)[-1]) {
+    for (i = 0; i < attIndex; i += 2)
+      if (appAtts[i] == elementType->idAtt->name) {
+        idAttIndex = i;
+        break;
+      }
+  }
+  else
+    idAttIndex = -1;
+
+  /* do attribute defaulting */
+  for (i = 0; i < nDefaultAtts; i++) {
+    const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i;
+    if (!(da->id->name)[-1] && da->value) {
+      if (da->id->prefix) {
+        if (da->id->xmlns) {
+          enum XML_Error result = addBinding(parser, da->id->prefix, da->id,
+                                             da->value, bindingsPtr);
+          if (result)
+            return result;
+        }
+        else {
+          (da->id->name)[-1] = 2;
+          nPrefixes++;
+          appAtts[attIndex++] = da->id->name;
+          appAtts[attIndex++] = da->value;
+        }
+      }
+      else {
+        (da->id->name)[-1] = 1;
+        appAtts[attIndex++] = da->id->name;
+        appAtts[attIndex++] = da->value;
+      }
+    }
+  }
+  appAtts[attIndex] = 0;
+
+  /* expand prefixed attribute names, check for duplicates,
+     and clear flags that say whether attributes were specified */
+  i = 0;
+  if (nPrefixes) {
+    int j;  /* hash table index */
+    unsigned long version = nsAttsVersion;
+    int nsAttsSize = (int)1 << nsAttsPower;
+    /* size of hash table must be at least 2 * (# of prefixed attributes) */
+    if ((nPrefixes << 1) >> nsAttsPower) {  /* true for nsAttsPower = 0 */
+      NS_ATT *temp;
+      /* hash table size must also be a power of 2 and >= 8 */
+      while (nPrefixes >> nsAttsPower++);
+      if (nsAttsPower < 3)
+        nsAttsPower = 3;
+      nsAttsSize = (int)1 << nsAttsPower;
+      temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT));
+      if (!temp)
+        return XML_ERROR_NO_MEMORY;
+      nsAtts = temp;
+      version = 0;  /* force re-initialization of nsAtts hash table */
+    }
+    /* using a version flag saves us from initializing nsAtts every time */
+    if (!version) {  /* initialize version flags when version wraps around */
+      version = INIT_ATTS_VERSION;
+      for (j = nsAttsSize; j != 0; )
+        nsAtts[--j].version = version;
+    }
+    nsAttsVersion = --version;
+
+    /* expand prefixed names and check for duplicates */
+    for (; i < attIndex; i += 2) {
+      const XML_Char *s = appAtts[i];
+      if (s[-1] == 2) {  /* prefixed */
+        ATTRIBUTE_ID *id;
+        const BINDING *b;
+        unsigned long uriHash = hash_secret_salt;
+        ((XML_Char *)s)[-1] = 0;  /* clear flag */
+        id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0);
+        if (!id || !id->prefix)
+          return XML_ERROR_NO_MEMORY;
+        b = id->prefix->binding;
+        if (!b)
+          return XML_ERROR_UNBOUND_PREFIX;
+
+        /* as we expand the name we also calculate its hash value */
+        for (j = 0; j < b->uriLen; j++) {
+          const XML_Char c = b->uri[j];
+          if (!poolAppendChar(&tempPool, c))
+            return XML_ERROR_NO_MEMORY;
+          uriHash = CHAR_HASH(uriHash, c);
+        }
+        while (*s++ != XML_T(ASCII_COLON))
+          ;
+        do {  /* copies null terminator */
+          const XML_Char c = *s;
+          if (!poolAppendChar(&tempPool, *s))
+            return XML_ERROR_NO_MEMORY;
+          uriHash = CHAR_HASH(uriHash, c);
+        } while (*s++);
+
+        { /* Check hash table for duplicate of expanded name (uriName).
+             Derived from code in lookup(parser, HASH_TABLE *table, ...).
+          */
+          unsigned char step = 0;
+          unsigned long mask = nsAttsSize - 1;
+          j = uriHash & mask;  /* index into hash table */
+          while (nsAtts[j].version == version) {
+            /* for speed we compare stored hash values first */
+            if (uriHash == nsAtts[j].hash) {
+              const XML_Char *s1 = poolStart(&tempPool);
+              const XML_Char *s2 = nsAtts[j].uriName;
+              /* s1 is null terminated, but not s2 */
+              for (; *s1 == *s2 && *s1 != 0; s1++, s2++);
+              if (*s1 == 0)
+                return XML_ERROR_DUPLICATE_ATTRIBUTE;
+            }
+            if (!step)
+              step = PROBE_STEP(uriHash, mask, nsAttsPower);
+            j < step ? (j += nsAttsSize - step) : (j -= step);
+          }
+        }
+
+        if (ns_triplets) {  /* append namespace separator and prefix */
+          tempPool.ptr[-1] = namespaceSeparator;
+          s = b->prefix->name;
+          do {
+            if (!poolAppendChar(&tempPool, *s))
+              return XML_ERROR_NO_MEMORY;
+          } while (*s++);
+        }
+
+        /* store expanded name in attribute list */
+        s = poolStart(&tempPool);
+        poolFinish(&tempPool);
+        appAtts[i] = s;
+
+        /* fill empty slot with new version, uriName and hash value */
+        nsAtts[j].version = version;
+        nsAtts[j].hash = uriHash;
+        nsAtts[j].uriName = s;
+
+        if (!--nPrefixes) {
+          i += 2;
+          break;
+        }
+      }
+      else  /* not prefixed */
+        ((XML_Char *)s)[-1] = 0;  /* clear flag */
+    }
+  }
+  /* clear flags for the remaining attributes */
+  for (; i < attIndex; i += 2)
+    ((XML_Char *)(appAtts[i]))[-1] = 0;
+  for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding)
+    binding->attId->name[-1] = 0;
+
+  if (!ns)
+    return XML_ERROR_NONE;
+
+  /* expand the element type name */
+  if (elementType->prefix) {
+    binding = elementType->prefix->binding;
+    if (!binding)
+      return XML_ERROR_UNBOUND_PREFIX;
+    localPart = tagNamePtr->str;
+    while (*localPart++ != XML_T(ASCII_COLON))
+      ;
+  }
+  else if (dtd->defaultPrefix.binding) {
+    binding = dtd->defaultPrefix.binding;
+    localPart = tagNamePtr->str;
+  }
+  else
+    return XML_ERROR_NONE;
+  prefixLen = 0;
+  if (ns_triplets && binding->prefix->name) {
+    for (; binding->prefix->name[prefixLen++];)
+      ;  /* prefixLen includes null terminator */
+  }
+  tagNamePtr->localPart = localPart;
+  tagNamePtr->uriLen = binding->uriLen;
+  tagNamePtr->prefix = binding->prefix->name;
+  tagNamePtr->prefixLen = prefixLen;
+  for (i = 0; localPart[i++];)
+    ;  /* i includes null terminator */
+  n = i + binding->uriLen + prefixLen;
+  if (n > binding->uriAlloc) {
+    TAG *p;
+    uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char));
+    if (!uri)
+      return XML_ERROR_NO_MEMORY;
+    binding->uriAlloc = n + EXPAND_SPARE;
+    memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char));
+    for (p = tagStack; p; p = p->parent)
+      if (p->name.str == binding->uri)
+        p->name.str = uri;
+    FREE(binding->uri);
+    binding->uri = uri;
+  }
+  /* if namespaceSeparator != '\0' then uri includes it already */
+  uri = binding->uri + binding->uriLen;
+  memcpy(uri, localPart, i * sizeof(XML_Char));
+  /* we always have a namespace separator between localPart and prefix */
+  if (prefixLen) {
+    uri += i - 1;
+    *uri = namespaceSeparator;  /* replace null terminator */
+    memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char));
+  }
+  tagNamePtr->str = binding->uri;
+  return XML_ERROR_NONE;
+}
+
+/* addBinding() overwrites the value of prefix->binding without checking.
+   Therefore one must keep track of the old value outside of addBinding().
+*/
+static enum XML_Error
+addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
+           const XML_Char *uri, BINDING **bindingsPtr)
+{
+  static const XML_Char xmlNamespace[] = {
+    ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH,
+    ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD,
+    ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L,
+    ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH,
+    ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c,
+    ASCII_e, '\0'
+  };
+  static const int xmlLen =
+    (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1;
+  static const XML_Char xmlnsNamespace[] = {
+    ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH,
+    ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD,
+    ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0,
+    ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s,
+    ASCII_SLASH, '\0'
+  };
+  static const int xmlnsLen =
+    (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1;
+
+  XML_Bool mustBeXML = XML_FALSE;
+  XML_Bool isXML = XML_TRUE;
+  XML_Bool isXMLNS = XML_TRUE;
+
+  BINDING *b;
+  int len;
+
+  /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */
+  if (*uri == XML_T('\0') && prefix->name)
+    return XML_ERROR_UNDECLARING_PREFIX;
+
+  if (prefix->name
+      && prefix->name[0] == XML_T(ASCII_x)
+      && prefix->name[1] == XML_T(ASCII_m)
+      && prefix->name[2] == XML_T(ASCII_l)) {
+
+    /* Not allowed to bind xmlns */
+    if (prefix->name[3] == XML_T(ASCII_n)
+        && prefix->name[4] == XML_T(ASCII_s)
+        && prefix->name[5] == XML_T('\0'))
+      return XML_ERROR_RESERVED_PREFIX_XMLNS;
+
+    if (prefix->name[3] == XML_T('\0'))
+      mustBeXML = XML_TRUE;
+  }
+
+  for (len = 0; uri[len]; len++) {
+    if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len]))
+      isXML = XML_FALSE;
+
+    if (!mustBeXML && isXMLNS
+        && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
+      isXMLNS = XML_FALSE;
+  }
+  isXML = isXML && len == xmlLen;
+  isXMLNS = isXMLNS && len == xmlnsLen;
+
+  if (mustBeXML != isXML)
+    return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML
+                     : XML_ERROR_RESERVED_NAMESPACE_URI;
+
+  if (isXMLNS)
+    return XML_ERROR_RESERVED_NAMESPACE_URI;
+
+  if (namespaceSeparator)
+    len++;
+  if (freeBindingList) {
+    b = freeBindingList;
+    if (len > b->uriAlloc) {
+      XML_Char *temp = (XML_Char *)REALLOC(b->uri,
+                          sizeof(XML_Char) * (len + EXPAND_SPARE));
+      if (temp == NULL)
+        return XML_ERROR_NO_MEMORY;
+      b->uri = temp;
+      b->uriAlloc = len + EXPAND_SPARE;
+    }
+    freeBindingList = b->nextTagBinding;
+  }
+  else {
+    b = (BINDING *)MALLOC(sizeof(BINDING));
+    if (!b)
+      return XML_ERROR_NO_MEMORY;
+    b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE));
+    if (!b->uri) {
+      FREE(b);
+      return XML_ERROR_NO_MEMORY;
+    }
+    b->uriAlloc = len + EXPAND_SPARE;
+  }
+  b->uriLen = len;
+  memcpy(b->uri, uri, len * sizeof(XML_Char));
+  if (namespaceSeparator)
+    b->uri[len - 1] = namespaceSeparator;
+  b->prefix = prefix;
+  b->attId = attId;
+  b->prevPrefixBinding = prefix->binding;
+  /* NULL binding when default namespace undeclared */
+  if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix)
+    prefix->binding = NULL;
+  else
+    prefix->binding = b;
+  b->nextTagBinding = *bindingsPtr;
+  *bindingsPtr = b;
+  /* if attId == NULL then we are not starting a namespace scope */
+  if (attId && startNamespaceDeclHandler)
+    startNamespaceDeclHandler(handlerArg, prefix->name,
+                              prefix->binding ? uri : 0);
+  return XML_ERROR_NONE;
+}
+
+/* The idea here is to avoid using stack for each CDATA section when
+   the whole file is parsed with one call.
+*/
+static enum XML_Error PTRCALL
+cdataSectionProcessor(XML_Parser parser,
+                      const char *start,
+                      const char *end,
+                      const char **endPtr)
+{
+  enum XML_Error result = doCdataSection(parser, encoding, &start, end,
+                                         endPtr, (XML_Bool)!ps_finalBuffer);
+  if (result != XML_ERROR_NONE)
+    return result;
+  if (start) {
+    if (parentParser) {  /* we are parsing an external entity */
+      processor = externalEntityContentProcessor;
+      return externalEntityContentProcessor(parser, start, end, endPtr);
+    }
+    else {
+      processor = contentProcessor;
+      return contentProcessor(parser, start, end, endPtr);
+    }
+  }
+  return result;
+}
+
+/* startPtr gets set to non-null if the section is closed, and to null if
+   the section is not yet closed.
+*/
+static enum XML_Error
+doCdataSection(XML_Parser parser,
+               const ENCODING *enc,
+               const char **startPtr,
+               const char *end,
+               const char **nextPtr,
+               XML_Bool haveMore)
+{
+  const char *s = *startPtr;
+  const char **eventPP;
+  const char **eventEndPP;
+  if (enc == encoding) {
+    eventPP = &eventPtr;
+    *eventPP = s;
+    eventEndPP = &eventEndPtr;
+  }
+  else {
+    eventPP = &(openInternalEntities->internalEventPtr);
+    eventEndPP = &(openInternalEntities->internalEventEndPtr);
+  }
+  *eventPP = s;
+  *startPtr = NULL;
+
+  for (;;) {
+    const char *next;
+    int tok = XmlCdataSectionTok(enc, s, end, &next);
+    *eventEndPP = next;
+    switch (tok) {
+    case XML_TOK_CDATA_SECT_CLOSE:
+      if (endCdataSectionHandler)
+        endCdataSectionHandler(handlerArg);
+#if 0
+      /* see comment under XML_TOK_CDATA_SECT_OPEN */
+      else if (characterDataHandler)
+        characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+      else if (defaultHandler)
+        reportDefault(parser, enc, s, next);
+      *startPtr = next;
+      *nextPtr = next;
+      if (ps_parsing == XML_FINISHED)
+        return XML_ERROR_ABORTED;
+      else
+        return XML_ERROR_NONE;
+    case XML_TOK_DATA_NEWLINE:
+      if (characterDataHandler) {
+        XML_Char c = 0xA;
+        characterDataHandler(handlerArg, &c, 1);
+      }
+      else if (defaultHandler)
+        reportDefault(parser, enc, s, next);
+      break;
+    case XML_TOK_DATA_CHARS:
+      {
+        XML_CharacterDataHandler charDataHandler = characterDataHandler;
+        if (charDataHandler) {
+          if (MUST_CONVERT(enc, s)) {
+            for (;;) {
+              ICHAR *dataPtr = (ICHAR *)dataBuf;
+              const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+              *eventEndPP = next;
+              charDataHandler(handlerArg, dataBuf,
+                              (int)(dataPtr - (ICHAR *)dataBuf));
+              if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
+                break;
+              *eventPP = s;
+            }
+          }
+          else
+            charDataHandler(handlerArg,
+                            (XML_Char *)s,
+                            (int)((XML_Char *)next - (XML_Char *)s));
+        }
+        else if (defaultHandler)
+          reportDefault(parser, enc, s, next);
+      }
+      break;
+    case XML_TOK_INVALID:
+      *eventPP = next;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL_CHAR:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_PARTIAL_CHAR;
+    case XML_TOK_PARTIAL:
+    case XML_TOK_NONE:
+      if (haveMore) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_UNCLOSED_CDATA_SECTION;
+    default:
+      *eventPP = next;
+      return XML_ERROR_UNEXPECTED_STATE;
+    }
+
+    *eventPP = s = next;
+    switch (ps_parsing) {
+    case XML_SUSPENDED:
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default: ;
+    }
+  }
+  /* not reached */
+}
+
+#ifdef XML_DTD
+
+/* The idea here is to avoid using stack for each IGNORE section when
+   the whole file is parsed with one call.
+*/
+static enum XML_Error PTRCALL
+ignoreSectionProcessor(XML_Parser parser,
+                       const char *start,
+                       const char *end,
+                       const char **endPtr)
+{
+  enum XML_Error result = doIgnoreSection(parser, encoding, &start, end,
+                                          endPtr, (XML_Bool)!ps_finalBuffer);
+  if (result != XML_ERROR_NONE)
+    return result;
+  if (start) {
+    processor = prologProcessor;
+    return prologProcessor(parser, start, end, endPtr);
+  }
+  return result;
+}
+
+/* startPtr gets set to non-null is the section is closed, and to null
+   if the section is not yet closed.
+*/
+static enum XML_Error
+doIgnoreSection(XML_Parser parser,
+                const ENCODING *enc,
+                const char **startPtr,
+                const char *end,
+                const char **nextPtr,
+                XML_Bool haveMore)
+{
+  const char *next;
+  int tok;
+  const char *s = *startPtr;
+  const char **eventPP;
+  const char **eventEndPP;
+  if (enc == encoding) {
+    eventPP = &eventPtr;
+    *eventPP = s;
+    eventEndPP = &eventEndPtr;
+  }
+  else {
+    eventPP = &(openInternalEntities->internalEventPtr);
+    eventEndPP = &(openInternalEntities->internalEventEndPtr);
+  }
+  *eventPP = s;
+  *startPtr = NULL;
+  tok = XmlIgnoreSectionTok(enc, s, end, &next);
+  *eventEndPP = next;
+  switch (tok) {
+  case XML_TOK_IGNORE_SECT:
+    if (defaultHandler)
+      reportDefault(parser, enc, s, next);
+    *startPtr = next;
+    *nextPtr = next;
+    if (ps_parsing == XML_FINISHED)
+      return XML_ERROR_ABORTED;
+    else
+      return XML_ERROR_NONE;
+  case XML_TOK_INVALID:
+    *eventPP = next;
+    return XML_ERROR_INVALID_TOKEN;
+  case XML_TOK_PARTIAL_CHAR:
+    if (haveMore) {
+      *nextPtr = s;
+      return XML_ERROR_NONE;
+    }
+    return XML_ERROR_PARTIAL_CHAR;
+  case XML_TOK_PARTIAL:
+  case XML_TOK_NONE:
+    if (haveMore) {
+      *nextPtr = s;
+      return XML_ERROR_NONE;
+    }
+    return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */
+  default:
+    *eventPP = next;
+    return XML_ERROR_UNEXPECTED_STATE;
+  }
+  /* not reached */
+}
+
+#endif /* XML_DTD */
+
+static enum XML_Error
+initializeEncoding(XML_Parser parser)
+{
+  const char *s;
+#ifdef XML_UNICODE
+  char encodingBuf[128];
+  if (!protocolEncodingName)
+    s = NULL;
+  else {
+    int i;
+    for (i = 0; protocolEncodingName[i]; i++) {
+      if (i == sizeof(encodingBuf) - 1
+          || (protocolEncodingName[i] & ~0x7f) != 0) {
+        encodingBuf[0] = '\0';
+        break;
+      }
+      encodingBuf[i] = (char)protocolEncodingName[i];
+    }
+    encodingBuf[i] = '\0';
+    s = encodingBuf;
+  }
+#else
+  s = protocolEncodingName;
+#endif
+  if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s))
+    return XML_ERROR_NONE;
+  return handleUnknownEncoding(parser, protocolEncodingName);
+}
+
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
+               const char *s, const char *next)
+{
+  const char *encodingName = NULL;
+  const XML_Char *storedEncName = NULL;
+  const ENCODING *newEncoding = NULL;
+  const char *version = NULL;
+  const char *versionend;
+  const XML_Char *storedversion = NULL;
+  int standalone = -1;
+  if (!(ns
+        ? XmlParseXmlDeclNS
+        : XmlParseXmlDecl)(isGeneralTextEntity,
+                           encoding,
+                           s,
+                           next,
+                           &eventPtr,
+                           &version,
+                           &versionend,
+                           &encodingName,
+                           &newEncoding,
+                           &standalone)) {
+    if (isGeneralTextEntity)
+      return XML_ERROR_TEXT_DECL;
+    else
+      return XML_ERROR_XML_DECL;
+  }
+  if (!isGeneralTextEntity && standalone == 1) {
+    _dtd->standalone = XML_TRUE;
+#ifdef XML_DTD
+    if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
+      paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+#endif /* XML_DTD */
+  }
+  if (xmlDeclHandler) {
+    if (encodingName != NULL) {
+      storedEncName = poolStoreString(&temp2Pool,
+                                      encoding,
+                                      encodingName,
+                                      encodingName
+                                      + XmlNameLength(encoding, encodingName));
+      if (!storedEncName)
+              return XML_ERROR_NO_MEMORY;
+      poolFinish(&temp2Pool);
+    }
+    if (version) {
+      storedversion = poolStoreString(&temp2Pool,
+                                      encoding,
+                                      version,
+                                      versionend - encoding->minBytesPerChar);
+      if (!storedversion)
+        return XML_ERROR_NO_MEMORY;
+    }
+    xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone);
+  }
+  else if (defaultHandler)
+    reportDefault(parser, encoding, s, next);
+  if (protocolEncodingName == NULL) {
+    if (newEncoding) {
+      if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) {
+        eventPtr = encodingName;
+        return XML_ERROR_INCORRECT_ENCODING;
+      }
+      encoding = newEncoding;
+    }
+    else if (encodingName) {
+      enum XML_Error result;
+      if (!storedEncName) {
+        storedEncName = poolStoreString(
+          &temp2Pool, encoding, encodingName,
+          encodingName + XmlNameLength(encoding, encodingName));
+        if (!storedEncName)
+          return XML_ERROR_NO_MEMORY;
+      }
+      result = handleUnknownEncoding(parser, storedEncName);
+      poolClear(&temp2Pool);
+      if (result == XML_ERROR_UNKNOWN_ENCODING)
+        eventPtr = encodingName;
+      return result;
+    }
+  }
+
+  if (storedEncName || storedversion)
+    poolClear(&temp2Pool);
+
+  return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName)
+{
+  if (unknownEncodingHandler) {
+    XML_Encoding info;
+    int i;
+    for (i = 0; i < 256; i++)
+      info.map[i] = -1;
+    info.convert = NULL;
+    info.data = NULL;
+    info.release = NULL;
+    if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName,
+                               &info)) {
+      ENCODING *enc;
+      unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding());
+      if (!unknownEncodingMem) {
+        if (info.release)
+          info.release(info.data);
+        return XML_ERROR_NO_MEMORY;
+      }
+      enc = (ns
+             ? XmlInitUnknownEncodingNS
+             : XmlInitUnknownEncoding)(unknownEncodingMem,
+                                       info.map,
+                                       info.convert,
+                                       info.data);
+      if (enc) {
+        unknownEncodingData = info.data;
+        unknownEncodingRelease = info.release;
+        encoding = enc;
+        return XML_ERROR_NONE;
+      }
+    }
+    if (info.release != NULL)
+      info.release(info.data);
+  }
+  return XML_ERROR_UNKNOWN_ENCODING;
+}
+
+static enum XML_Error PTRCALL
+prologInitProcessor(XML_Parser parser,
+                    const char *s,
+                    const char *end,
+                    const char **nextPtr)
+{
+  enum XML_Error result = initializeEncoding(parser);
+  if (result != XML_ERROR_NONE)
+    return result;
+  processor = prologProcessor;
+  return prologProcessor(parser, s, end, nextPtr);
+}
+
+#ifdef XML_DTD
+
+static enum XML_Error PTRCALL
+externalParEntInitProcessor(XML_Parser parser,
+                            const char *s,
+                            const char *end,
+                            const char **nextPtr)
+{
+  enum XML_Error result = initializeEncoding(parser);
+  if (result != XML_ERROR_NONE)
+    return result;
+
+  /* we know now that XML_Parse(Buffer) has been called,
+     so we consider the external parameter entity read */
+  _dtd->paramEntityRead = XML_TRUE;
+
+  if (prologState.inEntityValue) {
+    processor = entityValueInitProcessor;
+    return entityValueInitProcessor(parser, s, end, nextPtr);
+  }
+  else {
+    processor = externalParEntProcessor;
+    return externalParEntProcessor(parser, s, end, nextPtr);
+  }
+}
+
+static enum XML_Error PTRCALL
+entityValueInitProcessor(XML_Parser parser,
+                         const char *s,
+                         const char *end,
+                         const char **nextPtr)
+{
+  int tok;
+  const char *start = s;
+  const char *next = start;
+  eventPtr = start;
+
+  for (;;) {
+    tok = XmlPrologTok(encoding, start, end, &next);
+    eventEndPtr = next;
+    if (tok <= 0) {
+      if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      switch (tok) {
+      case XML_TOK_INVALID:
+        return XML_ERROR_INVALID_TOKEN;
+      case XML_TOK_PARTIAL:
+        return XML_ERROR_UNCLOSED_TOKEN;
+      case XML_TOK_PARTIAL_CHAR:
+        return XML_ERROR_PARTIAL_CHAR;
+      case XML_TOK_NONE:   /* start == end */
+      default:
+        break;
+      }
+      /* found end of entity value - can store it now */
+      return storeEntityValue(parser, encoding, s, end);
+    }
+    else if (tok == XML_TOK_XML_DECL) {
+      enum XML_Error result;
+      result = processXmlDecl(parser, 0, start, next);
+      if (result != XML_ERROR_NONE)
+        return result;
+      switch (ps_parsing) {
+      case XML_SUSPENDED:
+        *nextPtr = next;
+        return XML_ERROR_NONE;
+      case XML_FINISHED:
+        return XML_ERROR_ABORTED;
+      default:
+        *nextPtr = next;
+      }
+      /* stop scanning for text declaration - we found one */
+      processor = entityValueProcessor;
+      return entityValueProcessor(parser, next, end, nextPtr);
+    }
+    /* If we are at the end of the buffer, this would cause XmlPrologTok to
+       return XML_TOK_NONE on the next call, which would then cause the
+       function to exit with *nextPtr set to s - that is what we want for other
+       tokens, but not for the BOM - we would rather like to skip it;
+       then, when this routine is entered the next time, XmlPrologTok will
+       return XML_TOK_INVALID, since the BOM is still in the buffer
+    */
+    else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) {
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    }
+    start = next;
+    eventPtr = start;
+  }
+}
+
+static enum XML_Error PTRCALL
+externalParEntProcessor(XML_Parser parser,
+                        const char *s,
+                        const char *end,
+                        const char **nextPtr)
+{
+  const char *next = s;
+  int tok;
+
+  tok = XmlPrologTok(encoding, s, end, &next);
+  if (tok <= 0) {
+    if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+      *nextPtr = s;
+      return XML_ERROR_NONE;
+    }
+    switch (tok) {
+    case XML_TOK_INVALID:
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL:
+      return XML_ERROR_UNCLOSED_TOKEN;
+    case XML_TOK_PARTIAL_CHAR:
+      return XML_ERROR_PARTIAL_CHAR;
+    case XML_TOK_NONE:   /* start == end */
+    default:
+      break;
+    }
+  }
+  /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM.
+     However, when parsing an external subset, doProlog will not accept a BOM
+     as valid, and report a syntax error, so we have to skip the BOM
+  */
+  else if (tok == XML_TOK_BOM) {
+    s = next;
+    tok = XmlPrologTok(encoding, s, end, &next);
+  }
+
+  processor = prologProcessor;
+  return doProlog(parser, encoding, s, end, tok, next,
+                  nextPtr, (XML_Bool)!ps_finalBuffer);
+}
+
+static enum XML_Error PTRCALL
+entityValueProcessor(XML_Parser parser,
+                     const char *s,
+                     const char *end,
+                     const char **nextPtr)
+{
+  const char *start = s;
+  const char *next = s;
+  const ENCODING *enc = encoding;
+  int tok;
+
+  for (;;) {
+    tok = XmlPrologTok(enc, start, end, &next);
+    if (tok <= 0) {
+      if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      switch (tok) {
+      case XML_TOK_INVALID:
+        return XML_ERROR_INVALID_TOKEN;
+      case XML_TOK_PARTIAL:
+        return XML_ERROR_UNCLOSED_TOKEN;
+      case XML_TOK_PARTIAL_CHAR:
+        return XML_ERROR_PARTIAL_CHAR;
+      case XML_TOK_NONE:   /* start == end */
+      default:
+        break;
+      }
+      /* found end of entity value - can store it now */
+      return storeEntityValue(parser, enc, s, end);
+    }
+    start = next;
+  }
+}
+
+#endif /* XML_DTD */
+
+static enum XML_Error PTRCALL
+prologProcessor(XML_Parser parser,
+                const char *s,
+                const char *end,
+                const char **nextPtr)
+{
+  const char *next = s;
+  int tok = XmlPrologTok(encoding, s, end, &next);
+  return doProlog(parser, encoding, s, end, tok, next,
+                  nextPtr, (XML_Bool)!ps_finalBuffer);
+}
+
+static enum XML_Error
+doProlog(XML_Parser parser,
+         const ENCODING *enc,
+         const char *s,
+         const char *end,
+         int tok,
+         const char *next,
+         const char **nextPtr,
+         XML_Bool haveMore)
+{
+#ifdef XML_DTD
+  static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' };
+#endif /* XML_DTD */
+  static const XML_Char atypeCDATA[] =
+      { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+  static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' };
+  static const XML_Char atypeIDREF[] =
+      { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' };
+  static const XML_Char atypeIDREFS[] =
+      { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' };
+  static const XML_Char atypeENTITY[] =
+      { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' };
+  static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N,
+      ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' };
+  static const XML_Char atypeNMTOKEN[] = {
+      ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' };
+  static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T,
+      ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' };
+  static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T,
+      ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' };
+  static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' };
+  static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' };
+
+  /* save one level of indirection */
+  DTD * const dtd = _dtd;
+
+  const char **eventPP;
+  const char **eventEndPP;
+  enum XML_Content_Quant quant;
+
+  if (enc == encoding) {
+    eventPP = &eventPtr;
+    eventEndPP = &eventEndPtr;
+  }
+  else {
+    eventPP = &(openInternalEntities->internalEventPtr);
+    eventEndPP = &(openInternalEntities->internalEventEndPtr);
+  }
+
+  for (;;) {
+    int role;
+    XML_Bool handleDefault = XML_TRUE;
+    *eventPP = s;
+    *eventEndPP = next;
+    if (tok <= 0) {
+      if (haveMore && tok != XML_TOK_INVALID) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      switch (tok) {
+      case XML_TOK_INVALID:
+        *eventPP = next;
+        return XML_ERROR_INVALID_TOKEN;
+      case XML_TOK_PARTIAL:
+        return XML_ERROR_UNCLOSED_TOKEN;
+      case XML_TOK_PARTIAL_CHAR:
+        return XML_ERROR_PARTIAL_CHAR;
+      case -XML_TOK_PROLOG_S:
+        tok = -tok;
+        break;
+      case XML_TOK_NONE:
+#ifdef XML_DTD
+        /* for internal PE NOT referenced between declarations */
+        if (enc != encoding && !openInternalEntities->betweenDecl) {
+          *nextPtr = s;
+          return XML_ERROR_NONE;
+        }
+        /* WFC: PE Between Declarations - must check that PE contains
+           complete markup, not only for external PEs, but also for
+           internal PEs if the reference occurs between declarations.
+        */
+        if (isParamEntity || enc != encoding) {
+          if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc)
+              == XML_ROLE_ERROR)
+            return XML_ERROR_INCOMPLETE_PE;
+          *nextPtr = s;
+          return XML_ERROR_NONE;
+        }
+#endif /* XML_DTD */
+        return XML_ERROR_NO_ELEMENTS;
+      default:
+        tok = -tok;
+        next = end;
+        break;
+      }
+    }
+    role = XmlTokenRole(&prologState, tok, s, next, enc);
+    switch (role) {
+    case XML_ROLE_XML_DECL:
+      {
+        enum XML_Error result = processXmlDecl(parser, 0, s, next);
+        if (result != XML_ERROR_NONE)
+          return result;
+        enc = encoding;
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_DOCTYPE_NAME:
+      if (startDoctypeDeclHandler) {
+        doctypeName = poolStoreString(&tempPool, enc, s, next);
+        if (!doctypeName)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&tempPool);
+        doctypePubid = NULL;
+        handleDefault = XML_FALSE;
+      }
+      doctypeSysid = NULL; /* always initialize to NULL */
+      break;
+    case XML_ROLE_DOCTYPE_INTERNAL_SUBSET:
+      if (startDoctypeDeclHandler) {
+        startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid,
+                                doctypePubid, 1);
+        doctypeName = NULL;
+        poolClear(&tempPool);
+        handleDefault = XML_FALSE;
+      }
+      break;
+#ifdef XML_DTD
+    case XML_ROLE_TEXT_DECL:
+      {
+        enum XML_Error result = processXmlDecl(parser, 1, s, next);
+        if (result != XML_ERROR_NONE)
+          return result;
+        enc = encoding;
+        handleDefault = XML_FALSE;
+      }
+      break;
+#endif /* XML_DTD */
+    case XML_ROLE_DOCTYPE_PUBLIC_ID:
+#ifdef XML_DTD
+      useForeignDTD = XML_FALSE;
+      declEntity = (ENTITY *)lookup(parser,
+                                    &dtd->paramEntities,
+                                    externalSubsetName,
+                                    sizeof(ENTITY));
+      if (!declEntity)
+        return XML_ERROR_NO_MEMORY;
+#endif /* XML_DTD */
+      dtd->hasParamEntityRefs = XML_TRUE;
+      if (startDoctypeDeclHandler) {
+        XML_Char *pubId;
+        if (!XmlIsPublicId(enc, s, next, eventPP))
+          return XML_ERROR_PUBLICID;
+        pubId = poolStoreString(&tempPool, enc,
+                                s + enc->minBytesPerChar,
+                                next - enc->minBytesPerChar);
+        if (!pubId)
+          return XML_ERROR_NO_MEMORY;
+        normalizePublicId(pubId);
+        poolFinish(&tempPool);
+        doctypePubid = pubId;
+        handleDefault = XML_FALSE;
+        goto alreadyChecked;
+      }
+      /* fall through */
+    case XML_ROLE_ENTITY_PUBLIC_ID:
+      if (!XmlIsPublicId(enc, s, next, eventPP))
+        return XML_ERROR_PUBLICID;
+    alreadyChecked:
+      if (dtd->keepProcessing && declEntity) {
+        XML_Char *tem = poolStoreString(&dtd->pool,
+                                        enc,
+                                        s + enc->minBytesPerChar,
+                                        next - enc->minBytesPerChar);
+        if (!tem)
+          return XML_ERROR_NO_MEMORY;
+        normalizePublicId(tem);
+        declEntity->publicId = tem;
+        poolFinish(&dtd->pool);
+        if (entityDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_DOCTYPE_CLOSE:
+      if (doctypeName) {
+        startDoctypeDeclHandler(handlerArg, doctypeName,
+                                doctypeSysid, doctypePubid, 0);
+        poolClear(&tempPool);
+        handleDefault = XML_FALSE;
+      }
+      /* doctypeSysid will be non-NULL in the case of a previous
+         XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler
+         was not set, indicating an external subset
+      */
+#ifdef XML_DTD
+      if (doctypeSysid || useForeignDTD) {
+        XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
+        dtd->hasParamEntityRefs = XML_TRUE;
+        if (paramEntityParsing && externalEntityRefHandler) {
+          ENTITY *entity = (ENTITY *)lookup(parser,
+                                            &dtd->paramEntities,
+                                            externalSubsetName,
+                                            sizeof(ENTITY));
+          if (!entity)
+            return XML_ERROR_NO_MEMORY;
+          if (useForeignDTD)
+            entity->base = curBase;
+          dtd->paramEntityRead = XML_FALSE;
+          if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+                                        0,
+                                        entity->base,
+                                        entity->systemId,
+                                        entity->publicId))
+            return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+          if (dtd->paramEntityRead) {
+            if (!dtd->standalone &&
+                notStandaloneHandler &&
+                !notStandaloneHandler(handlerArg))
+              return XML_ERROR_NOT_STANDALONE;
+          }
+          /* if we didn't read the foreign DTD then this means that there
+             is no external subset and we must reset dtd->hasParamEntityRefs
+          */
+          else if (!doctypeSysid)
+            dtd->hasParamEntityRefs = hadParamEntityRefs;
+          /* end of DTD - no need to update dtd->keepProcessing */
+        }
+        useForeignDTD = XML_FALSE;
+      }
+#endif /* XML_DTD */
+      if (endDoctypeDeclHandler) {
+        endDoctypeDeclHandler(handlerArg);
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_INSTANCE_START:
+#ifdef XML_DTD
+      /* if there is no DOCTYPE declaration then now is the
+         last chance to read the foreign DTD
+      */
+      if (useForeignDTD) {
+        XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
+        dtd->hasParamEntityRefs = XML_TRUE;
+        if (paramEntityParsing && externalEntityRefHandler) {
+          ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities,
+                                            externalSubsetName,
+                                            sizeof(ENTITY));
+          if (!entity)
+            return XML_ERROR_NO_MEMORY;
+          entity->base = curBase;
+          dtd->paramEntityRead = XML_FALSE;
+          if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+                                        0,
+                                        entity->base,
+                                        entity->systemId,
+                                        entity->publicId))
+            return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+          if (dtd->paramEntityRead) {
+            if (!dtd->standalone &&
+                notStandaloneHandler &&
+                !notStandaloneHandler(handlerArg))
+              return XML_ERROR_NOT_STANDALONE;
+          }
+          /* if we didn't read the foreign DTD then this means that there
+             is no external subset and we must reset dtd->hasParamEntityRefs
+          */
+          else
+            dtd->hasParamEntityRefs = hadParamEntityRefs;
+          /* end of DTD - no need to update dtd->keepProcessing */
+        }
+      }
+#endif /* XML_DTD */
+      processor = contentProcessor;
+      return contentProcessor(parser, s, end, nextPtr);
+    case XML_ROLE_ATTLIST_ELEMENT_NAME:
+      declElementType = getElementType(parser, enc, s, next);
+      if (!declElementType)
+        return XML_ERROR_NO_MEMORY;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_NAME:
+      declAttributeId = getAttributeId(parser, enc, s, next);
+      if (!declAttributeId)
+        return XML_ERROR_NO_MEMORY;
+      declAttributeIsCdata = XML_FALSE;
+      declAttributeType = NULL;
+      declAttributeIsId = XML_FALSE;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_CDATA:
+      declAttributeIsCdata = XML_TRUE;
+      declAttributeType = atypeCDATA;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_ID:
+      declAttributeIsId = XML_TRUE;
+      declAttributeType = atypeID;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_IDREF:
+      declAttributeType = atypeIDREF;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_IDREFS:
+      declAttributeType = atypeIDREFS;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_ENTITY:
+      declAttributeType = atypeENTITY;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES:
+      declAttributeType = atypeENTITIES;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN:
+      declAttributeType = atypeNMTOKEN;
+      goto checkAttListDeclHandler;
+    case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS:
+      declAttributeType = atypeNMTOKENS;
+    checkAttListDeclHandler:
+      if (dtd->keepProcessing && attlistDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_ATTRIBUTE_ENUM_VALUE:
+    case XML_ROLE_ATTRIBUTE_NOTATION_VALUE:
+      if (dtd->keepProcessing && attlistDeclHandler) {
+        const XML_Char *prefix;
+        if (declAttributeType) {
+          prefix = enumValueSep;
+        }
+        else {
+          prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE
+                    ? notationPrefix
+                    : enumValueStart);
+        }
+        if (!poolAppendString(&tempPool, prefix))
+          return XML_ERROR_NO_MEMORY;
+        if (!poolAppend(&tempPool, enc, s, next))
+          return XML_ERROR_NO_MEMORY;
+        declAttributeType = tempPool.start;
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE:
+    case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE:
+      if (dtd->keepProcessing) {
+        if (!defineAttribute(declElementType, declAttributeId,
+                             declAttributeIsCdata, declAttributeIsId,
+                             0, parser))
+          return XML_ERROR_NO_MEMORY;
+        if (attlistDeclHandler && declAttributeType) {
+          if (*declAttributeType == XML_T(ASCII_LPAREN)
+              || (*declAttributeType == XML_T(ASCII_N)
+                  && declAttributeType[1] == XML_T(ASCII_O))) {
+            /* Enumerated or Notation type */
+            if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN))
+                || !poolAppendChar(&tempPool, XML_T('\0')))
+              return XML_ERROR_NO_MEMORY;
+            declAttributeType = tempPool.start;
+            poolFinish(&tempPool);
+          }
+          *eventEndPP = s;
+          attlistDeclHandler(handlerArg, declElementType->name,
+                             declAttributeId->name, declAttributeType,
+                             0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE);
+          poolClear(&tempPool);
+          handleDefault = XML_FALSE;
+        }
+      }
+      break;
+    case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE:
+    case XML_ROLE_FIXED_ATTRIBUTE_VALUE:
+      if (dtd->keepProcessing) {
+        const XML_Char *attVal;
+        enum XML_Error result =
+          storeAttributeValue(parser, enc, declAttributeIsCdata,
+                              s + enc->minBytesPerChar,
+                              next - enc->minBytesPerChar,
+                              &dtd->pool);
+        if (result)
+          return result;
+        attVal = poolStart(&dtd->pool);
+        poolFinish(&dtd->pool);
+        /* ID attributes aren't allowed to have a default */
+        if (!defineAttribute(declElementType, declAttributeId,
+                             declAttributeIsCdata, XML_FALSE, attVal, parser))
+          return XML_ERROR_NO_MEMORY;
+        if (attlistDeclHandler && declAttributeType) {
+          if (*declAttributeType == XML_T(ASCII_LPAREN)
+              || (*declAttributeType == XML_T(ASCII_N)
+                  && declAttributeType[1] == XML_T(ASCII_O))) {
+            /* Enumerated or Notation type */
+            if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN))
+                || !poolAppendChar(&tempPool, XML_T('\0')))
+              return XML_ERROR_NO_MEMORY;
+            declAttributeType = tempPool.start;
+            poolFinish(&tempPool);
+          }
+          *eventEndPP = s;
+          attlistDeclHandler(handlerArg, declElementType->name,
+                             declAttributeId->name, declAttributeType,
+                             attVal,
+                             role == XML_ROLE_FIXED_ATTRIBUTE_VALUE);
+          poolClear(&tempPool);
+          handleDefault = XML_FALSE;
+        }
+      }
+      break;
+    case XML_ROLE_ENTITY_VALUE:
+      if (dtd->keepProcessing) {
+        enum XML_Error result = storeEntityValue(parser, enc,
+                                            s + enc->minBytesPerChar,
+                                            next - enc->minBytesPerChar);
+        if (declEntity) {
+          declEntity->textPtr = poolStart(&dtd->entityValuePool);
+          declEntity->textLen = (int)(poolLength(&dtd->entityValuePool));
+          poolFinish(&dtd->entityValuePool);
+          if (entityDeclHandler) {
+            *eventEndPP = s;
+            entityDeclHandler(handlerArg,
+                              declEntity->name,
+                              declEntity->is_param,
+                              declEntity->textPtr,
+                              declEntity->textLen,
+                              curBase, 0, 0, 0);
+            handleDefault = XML_FALSE;
+          }
+        }
+        else
+          poolDiscard(&dtd->entityValuePool);
+        if (result != XML_ERROR_NONE)
+          return result;
+      }
+      break;
+    case XML_ROLE_DOCTYPE_SYSTEM_ID:
+#ifdef XML_DTD
+      useForeignDTD = XML_FALSE;
+#endif /* XML_DTD */
+      dtd->hasParamEntityRefs = XML_TRUE;
+      if (startDoctypeDeclHandler) {
+        doctypeSysid = poolStoreString(&tempPool, enc,
+                                       s + enc->minBytesPerChar,
+                                       next - enc->minBytesPerChar);
+        if (doctypeSysid == NULL)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&tempPool);
+        handleDefault = XML_FALSE;
+      }
+#ifdef XML_DTD
+      else
+        /* use externalSubsetName to make doctypeSysid non-NULL
+           for the case where no startDoctypeDeclHandler is set */
+        doctypeSysid = externalSubsetName;
+#endif /* XML_DTD */
+      if (!dtd->standalone
+#ifdef XML_DTD
+          && !paramEntityParsing
+#endif /* XML_DTD */
+          && notStandaloneHandler
+          && !notStandaloneHandler(handlerArg))
+        return XML_ERROR_NOT_STANDALONE;
+#ifndef XML_DTD
+      break;
+#else /* XML_DTD */
+      if (!declEntity) {
+        declEntity = (ENTITY *)lookup(parser,
+                                      &dtd->paramEntities,
+                                      externalSubsetName,
+                                      sizeof(ENTITY));
+        if (!declEntity)
+          return XML_ERROR_NO_MEMORY;
+        declEntity->publicId = NULL;
+      }
+      /* fall through */
+#endif /* XML_DTD */
+    case XML_ROLE_ENTITY_SYSTEM_ID:
+      if (dtd->keepProcessing && declEntity) {
+        declEntity->systemId = poolStoreString(&dtd->pool, enc,
+                                               s + enc->minBytesPerChar,
+                                               next - enc->minBytesPerChar);
+        if (!declEntity->systemId)
+          return XML_ERROR_NO_MEMORY;
+        declEntity->base = curBase;
+        poolFinish(&dtd->pool);
+        if (entityDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_ENTITY_COMPLETE:
+      if (dtd->keepProcessing && declEntity && entityDeclHandler) {
+        *eventEndPP = s;
+        entityDeclHandler(handlerArg,
+                          declEntity->name,
+                          declEntity->is_param,
+                          0,0,
+                          declEntity->base,
+                          declEntity->systemId,
+                          declEntity->publicId,
+                          0);
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_ENTITY_NOTATION_NAME:
+      if (dtd->keepProcessing && declEntity) {
+        declEntity->notation = poolStoreString(&dtd->pool, enc, s, next);
+        if (!declEntity->notation)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&dtd->pool);
+        if (unparsedEntityDeclHandler) {
+          *eventEndPP = s;
+          unparsedEntityDeclHandler(handlerArg,
+                                    declEntity->name,
+                                    declEntity->base,
+                                    declEntity->systemId,
+                                    declEntity->publicId,
+                                    declEntity->notation);
+          handleDefault = XML_FALSE;
+        }
+        else if (entityDeclHandler) {
+          *eventEndPP = s;
+          entityDeclHandler(handlerArg,
+                            declEntity->name,
+                            0,0,0,
+                            declEntity->base,
+                            declEntity->systemId,
+                            declEntity->publicId,
+                            declEntity->notation);
+          handleDefault = XML_FALSE;
+        }
+      }
+      break;
+    case XML_ROLE_GENERAL_ENTITY_NAME:
+      {
+        if (XmlPredefinedEntityName(enc, s, next)) {
+          declEntity = NULL;
+          break;
+        }
+        if (dtd->keepProcessing) {
+          const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
+          if (!name)
+            return XML_ERROR_NO_MEMORY;
+          declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name,
+                                        sizeof(ENTITY));
+          if (!declEntity)
+            return XML_ERROR_NO_MEMORY;
+          if (declEntity->name != name) {
+            poolDiscard(&dtd->pool);
+            declEntity = NULL;
+          }
+          else {
+            poolFinish(&dtd->pool);
+            declEntity->publicId = NULL;
+            declEntity->is_param = XML_FALSE;
+            /* if we have a parent parser or are reading an internal parameter
+               entity, then the entity declaration is not considered "internal"
+            */
+            declEntity->is_internal = !(parentParser || openInternalEntities);
+            if (entityDeclHandler)
+              handleDefault = XML_FALSE;
+          }
+        }
+        else {
+          poolDiscard(&dtd->pool);
+          declEntity = NULL;
+        }
+      }
+      break;
+    case XML_ROLE_PARAM_ENTITY_NAME:
+#ifdef XML_DTD
+      if (dtd->keepProcessing) {
+        const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
+        if (!name)
+          return XML_ERROR_NO_MEMORY;
+        declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities,
+                                           name, sizeof(ENTITY));
+        if (!declEntity)
+          return XML_ERROR_NO_MEMORY;
+        if (declEntity->name != name) {
+          poolDiscard(&dtd->pool);
+          declEntity = NULL;
+        }
+        else {
+          poolFinish(&dtd->pool);
+          declEntity->publicId = NULL;
+          declEntity->is_param = XML_TRUE;
+          /* if we have a parent parser or are reading an internal parameter
+             entity, then the entity declaration is not considered "internal"
+          */
+          declEntity->is_internal = !(parentParser || openInternalEntities);
+          if (entityDeclHandler)
+            handleDefault = XML_FALSE;
+        }
+      }
+      else {
+        poolDiscard(&dtd->pool);
+        declEntity = NULL;
+      }
+#else /* not XML_DTD */
+      declEntity = NULL;
+#endif /* XML_DTD */
+      break;
+    case XML_ROLE_NOTATION_NAME:
+      declNotationPublicId = NULL;
+      declNotationName = NULL;
+      if (notationDeclHandler) {
+        declNotationName = poolStoreString(&tempPool, enc, s, next);
+        if (!declNotationName)
+          return XML_ERROR_NO_MEMORY;
+        poolFinish(&tempPool);
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_NOTATION_PUBLIC_ID:
+      if (!XmlIsPublicId(enc, s, next, eventPP))
+        return XML_ERROR_PUBLICID;
+      if (declNotationName) {  /* means notationDeclHandler != NULL */
+        XML_Char *tem = poolStoreString(&tempPool,
+                                        enc,
+                                        s + enc->minBytesPerChar,
+                                        next - enc->minBytesPerChar);
+        if (!tem)
+          return XML_ERROR_NO_MEMORY;
+        normalizePublicId(tem);
+        declNotationPublicId = tem;
+        poolFinish(&tempPool);
+        handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_NOTATION_SYSTEM_ID:
+      if (declNotationName && notationDeclHandler) {
+        const XML_Char *systemId
+          = poolStoreString(&tempPool, enc,
+                            s + enc->minBytesPerChar,
+                            next - enc->minBytesPerChar);
+        if (!systemId)
+          return XML_ERROR_NO_MEMORY;
+        *eventEndPP = s;
+        notationDeclHandler(handlerArg,
+                            declNotationName,
+                            curBase,
+                            systemId,
+                            declNotationPublicId);
+        handleDefault = XML_FALSE;
+      }
+      poolClear(&tempPool);
+      break;
+    case XML_ROLE_NOTATION_NO_SYSTEM_ID:
+      if (declNotationPublicId && notationDeclHandler) {
+        *eventEndPP = s;
+        notationDeclHandler(handlerArg,
+                            declNotationName,
+                            curBase,
+                            0,
+                            declNotationPublicId);
+        handleDefault = XML_FALSE;
+      }
+      poolClear(&tempPool);
+      break;
+    case XML_ROLE_ERROR:
+      switch (tok) {
+      case XML_TOK_PARAM_ENTITY_REF:
+        /* PE references in internal subset are
+           not allowed within declarations. */
+        return XML_ERROR_PARAM_ENTITY_REF;
+      case XML_TOK_XML_DECL:
+        return XML_ERROR_MISPLACED_XML_PI;
+      default:
+        return XML_ERROR_SYNTAX;
+      }
+#ifdef XML_DTD
+    case XML_ROLE_IGNORE_SECT:
+      {
+        enum XML_Error result;
+        if (defaultHandler)
+          reportDefault(parser, enc, s, next);
+        handleDefault = XML_FALSE;
+        result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore);
+        if (result != XML_ERROR_NONE)
+          return result;
+        else if (!next) {
+          processor = ignoreSectionProcessor;
+          return result;
+        }
+      }
+      break;
+#endif /* XML_DTD */
+    case XML_ROLE_GROUP_OPEN:
+      if (prologState.level >= groupSize) {
+        if (groupSize) {
+          char *temp = (char *)REALLOC(groupConnector, groupSize *= 2);
+          if (temp == NULL)
+            return XML_ERROR_NO_MEMORY;
+          groupConnector = temp;
+          if (dtd->scaffIndex) {
+            int *temp = (int *)REALLOC(dtd->scaffIndex,
+                          groupSize * sizeof(int));
+            if (temp == NULL)
+              return XML_ERROR_NO_MEMORY;
+            dtd->scaffIndex = temp;
+          }
+        }
+        else {
+          groupConnector = (char *)MALLOC(groupSize = 32);
+          if (!groupConnector)
+            return XML_ERROR_NO_MEMORY;
+        }
+      }
+      groupConnector[prologState.level] = 0;
+      if (dtd->in_eldecl) {
+        int myindex = nextScaffoldPart(parser);
+        if (myindex < 0)
+          return XML_ERROR_NO_MEMORY;
+        dtd->scaffIndex[dtd->scaffLevel] = myindex;
+        dtd->scaffLevel++;
+        dtd->scaffold[myindex].type = XML_CTYPE_SEQ;
+        if (elementDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      break;
+    case XML_ROLE_GROUP_SEQUENCE:
+      if (groupConnector[prologState.level] == ASCII_PIPE)
+        return XML_ERROR_SYNTAX;
+      groupConnector[prologState.level] = ASCII_COMMA;
+      if (dtd->in_eldecl && elementDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_GROUP_CHOICE:
+      if (groupConnector[prologState.level] == ASCII_COMMA)
+        return XML_ERROR_SYNTAX;
+      if (dtd->in_eldecl
+          && !groupConnector[prologState.level]
+          && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+              != XML_CTYPE_MIXED)
+          ) {
+        dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+            = XML_CTYPE_CHOICE;
+        if (elementDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      groupConnector[prologState.level] = ASCII_PIPE;
+      break;
+    case XML_ROLE_PARAM_ENTITY_REF:
+#ifdef XML_DTD
+    case XML_ROLE_INNER_PARAM_ENTITY_REF:
+      dtd->hasParamEntityRefs = XML_TRUE;
+      if (!paramEntityParsing)
+        dtd->keepProcessing = dtd->standalone;
+      else {
+        const XML_Char *name;
+        ENTITY *entity;
+        name = poolStoreString(&dtd->pool, enc,
+                                s + enc->minBytesPerChar,
+                                next - enc->minBytesPerChar);
+        if (!name)
+          return XML_ERROR_NO_MEMORY;
+        entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0);
+        poolDiscard(&dtd->pool);
+        /* first, determine if a check for an existing declaration is needed;
+           if yes, check that the entity exists, and that it is internal,
+           otherwise call the skipped entity handler
+        */
+        if (prologState.documentEntity &&
+            (dtd->standalone
+             ? !openInternalEntities
+             : !dtd->hasParamEntityRefs)) {
+          if (!entity)
+            return XML_ERROR_UNDEFINED_ENTITY;
+          else if (!entity->is_internal)
+            return XML_ERROR_ENTITY_DECLARED_IN_PE;
+        }
+        else if (!entity) {
+          dtd->keepProcessing = dtd->standalone;
+          /* cannot report skipped entities in declarations */
+          if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) {
+            skippedEntityHandler(handlerArg, name, 1);
+            handleDefault = XML_FALSE;
+          }
+          break;
+        }
+        if (entity->open)
+          return XML_ERROR_RECURSIVE_ENTITY_REF;
+        if (entity->textPtr) {
+          enum XML_Error result;
+          XML_Bool betweenDecl =
+            (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE);
+          result = processInternalEntity(parser, entity, betweenDecl);
+          if (result != XML_ERROR_NONE)
+            return result;
+          handleDefault = XML_FALSE;
+          break;
+        }
+        if (externalEntityRefHandler) {
+          dtd->paramEntityRead = XML_FALSE;
+          entity->open = XML_TRUE;
+          if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+                                        0,
+                                        entity->base,
+                                        entity->systemId,
+                                        entity->publicId)) {
+            entity->open = XML_FALSE;
+            return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+          }
+          entity->open = XML_FALSE;
+          handleDefault = XML_FALSE;
+          if (!dtd->paramEntityRead) {
+            dtd->keepProcessing = dtd->standalone;
+            break;
+          }
+        }
+        else {
+          dtd->keepProcessing = dtd->standalone;
+          break;
+        }
+      }
+#endif /* XML_DTD */
+      if (!dtd->standalone &&
+          notStandaloneHandler &&
+          !notStandaloneHandler(handlerArg))
+        return XML_ERROR_NOT_STANDALONE;
+      break;
+
+    /* Element declaration stuff */
+
+    case XML_ROLE_ELEMENT_NAME:
+      if (elementDeclHandler) {
+        declElementType = getElementType(parser, enc, s, next);
+        if (!declElementType)
+          return XML_ERROR_NO_MEMORY;
+        dtd->scaffLevel = 0;
+        dtd->scaffCount = 0;
+        dtd->in_eldecl = XML_TRUE;
+        handleDefault = XML_FALSE;
+      }
+      break;
+
+    case XML_ROLE_CONTENT_ANY:
+    case XML_ROLE_CONTENT_EMPTY:
+      if (dtd->in_eldecl) {
+        if (elementDeclHandler) {
+          XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content));
+          if (!content)
+            return XML_ERROR_NO_MEMORY;
+          content->quant = XML_CQUANT_NONE;
+          content->name = NULL;
+          content->numchildren = 0;
+          content->children = NULL;
+          content->type = ((role == XML_ROLE_CONTENT_ANY) ?
+                           XML_CTYPE_ANY :
+                           XML_CTYPE_EMPTY);
+          *eventEndPP = s;
+          elementDeclHandler(handlerArg, declElementType->name, content);
+          handleDefault = XML_FALSE;
+        }
+        dtd->in_eldecl = XML_FALSE;
+      }
+      break;
+
+    case XML_ROLE_CONTENT_PCDATA:
+      if (dtd->in_eldecl) {
+        dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
+            = XML_CTYPE_MIXED;
+        if (elementDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      break;
+
+    case XML_ROLE_CONTENT_ELEMENT:
+      quant = XML_CQUANT_NONE;
+      goto elementContent;
+    case XML_ROLE_CONTENT_ELEMENT_OPT:
+      quant = XML_CQUANT_OPT;
+      goto elementContent;
+    case XML_ROLE_CONTENT_ELEMENT_REP:
+      quant = XML_CQUANT_REP;
+      goto elementContent;
+    case XML_ROLE_CONTENT_ELEMENT_PLUS:
+      quant = XML_CQUANT_PLUS;
+    elementContent:
+      if (dtd->in_eldecl) {
+        ELEMENT_TYPE *el;
+        const XML_Char *name;
+        int nameLen;
+        const char *nxt = (quant == XML_CQUANT_NONE
+                           ? next
+                           : next - enc->minBytesPerChar);
+        int myindex = nextScaffoldPart(parser);
+        if (myindex < 0)
+          return XML_ERROR_NO_MEMORY;
+        dtd->scaffold[myindex].type = XML_CTYPE_NAME;
+        dtd->scaffold[myindex].quant = quant;
+        el = getElementType(parser, enc, s, nxt);
+        if (!el)
+          return XML_ERROR_NO_MEMORY;
+        name = el->name;
+        dtd->scaffold[myindex].name = name;
+        nameLen = 0;
+        for (; name[nameLen++]; );
+        dtd->contentStringLen +=  nameLen;
+        if (elementDeclHandler)
+          handleDefault = XML_FALSE;
+      }
+      break;
+
+    case XML_ROLE_GROUP_CLOSE:
+      quant = XML_CQUANT_NONE;
+      goto closeGroup;
+    case XML_ROLE_GROUP_CLOSE_OPT:
+      quant = XML_CQUANT_OPT;
+      goto closeGroup;
+    case XML_ROLE_GROUP_CLOSE_REP:
+      quant = XML_CQUANT_REP;
+      goto closeGroup;
+    case XML_ROLE_GROUP_CLOSE_PLUS:
+      quant = XML_CQUANT_PLUS;
+    closeGroup:
+      if (dtd->in_eldecl) {
+        if (elementDeclHandler)
+          handleDefault = XML_FALSE;
+        dtd->scaffLevel--;
+        dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant;
+        if (dtd->scaffLevel == 0) {
+          if (!handleDefault) {
+            XML_Content *model = build_model(parser);
+            if (!model)
+              return XML_ERROR_NO_MEMORY;
+            *eventEndPP = s;
+            elementDeclHandler(handlerArg, declElementType->name, model);
+          }
+          dtd->in_eldecl = XML_FALSE;
+          dtd->contentStringLen = 0;
+        }
+      }
+      break;
+      /* End element declaration stuff */
+
+    case XML_ROLE_PI:
+      if (!reportProcessingInstruction(parser, enc, s, next))
+        return XML_ERROR_NO_MEMORY;
+      handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_COMMENT:
+      if (!reportComment(parser, enc, s, next))
+        return XML_ERROR_NO_MEMORY;
+      handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_NONE:
+      switch (tok) {
+      case XML_TOK_BOM:
+        handleDefault = XML_FALSE;
+        break;
+      }
+      break;
+    case XML_ROLE_DOCTYPE_NONE:
+      if (startDoctypeDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_ENTITY_NONE:
+      if (dtd->keepProcessing && entityDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_NOTATION_NONE:
+      if (notationDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_ATTLIST_NONE:
+      if (dtd->keepProcessing && attlistDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    case XML_ROLE_ELEMENT_NONE:
+      if (elementDeclHandler)
+        handleDefault = XML_FALSE;
+      break;
+    } /* end of big switch */
+
+    if (handleDefault && defaultHandler)
+      reportDefault(parser, enc, s, next);
+
+    switch (ps_parsing) {
+    case XML_SUSPENDED:
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default:
+      s = next;
+      tok = XmlPrologTok(enc, s, end, &next);
+    }
+  }
+  /* not reached */
+}
+
+static enum XML_Error PTRCALL
+epilogProcessor(XML_Parser parser,
+                const char *s,
+                const char *end,
+                const char **nextPtr)
+{
+  processor = epilogProcessor;
+  eventPtr = s;
+  for (;;) {
+    const char *next = NULL;
+    int tok = XmlPrologTok(encoding, s, end, &next);
+    eventEndPtr = next;
+    switch (tok) {
+    /* report partial linebreak - it might be the last token */
+    case -XML_TOK_PROLOG_S:
+      if (defaultHandler) {
+        reportDefault(parser, encoding, s, next);
+        if (ps_parsing == XML_FINISHED)
+          return XML_ERROR_ABORTED;
+      }
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_TOK_NONE:
+      *nextPtr = s;
+      return XML_ERROR_NONE;
+    case XML_TOK_PROLOG_S:
+      if (defaultHandler)
+        reportDefault(parser, encoding, s, next);
+      break;
+    case XML_TOK_PI:
+      if (!reportProcessingInstruction(parser, encoding, s, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_COMMENT:
+      if (!reportComment(parser, encoding, s, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_INVALID:
+      eventPtr = next;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL:
+      if (!ps_finalBuffer) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_UNCLOSED_TOKEN;
+    case XML_TOK_PARTIAL_CHAR:
+      if (!ps_finalBuffer) {
+        *nextPtr = s;
+        return XML_ERROR_NONE;
+      }
+      return XML_ERROR_PARTIAL_CHAR;
+    default:
+      return XML_ERROR_JUNK_AFTER_DOC_ELEMENT;
+    }
+    eventPtr = s = next;
+    switch (ps_parsing) {
+    case XML_SUSPENDED:
+      *nextPtr = next;
+      return XML_ERROR_NONE;
+    case XML_FINISHED:
+      return XML_ERROR_ABORTED;
+    default: ;
+    }
+  }
+}
+
+static enum XML_Error
+processInternalEntity(XML_Parser parser, ENTITY *entity,
+                      XML_Bool betweenDecl)
+{
+  const char *textStart, *textEnd;
+  const char *next;
+  enum XML_Error result;
+  OPEN_INTERNAL_ENTITY *openEntity;
+
+  if (freeInternalEntities) {
+    openEntity = freeInternalEntities;
+    freeInternalEntities = openEntity->next;
+  }
+  else {
+    openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY));
+    if (!openEntity)
+      return XML_ERROR_NO_MEMORY;
+  }
+  entity->open = XML_TRUE;
+  entity->processed = 0;
+  openEntity->next = openInternalEntities;
+  openInternalEntities = openEntity;
+  openEntity->entity = entity;
+  openEntity->startTagLevel = tagLevel;
+  openEntity->betweenDecl = betweenDecl;
+  openEntity->internalEventPtr = NULL;
+  openEntity->internalEventEndPtr = NULL;
+  textStart = (char *)entity->textPtr;
+  textEnd = (char *)(entity->textPtr + entity->textLen);
+
+#ifdef XML_DTD
+  if (entity->is_param) {
+    int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next);
+    result = doProlog(parser, internalEncoding, textStart, textEnd, tok,
+                      next, &next, XML_FALSE);
+  }
+  else
+#endif /* XML_DTD */
+    result = doContent(parser, tagLevel, internalEncoding, textStart,
+                       textEnd, &next, XML_FALSE);
+
+  if (result == XML_ERROR_NONE) {
+    if (textEnd != next && ps_parsing == XML_SUSPENDED) {
+      entity->processed = (int)(next - textStart);
+      processor = internalEntityProcessor;
+    }
+    else {
+      entity->open = XML_FALSE;
+      openInternalEntities = openEntity->next;
+      /* put openEntity back in list of free instances */
+      openEntity->next = freeInternalEntities;
+      freeInternalEntities = openEntity;
+    }
+  }
+  return result;
+}
+
+static enum XML_Error PTRCALL
+internalEntityProcessor(XML_Parser parser,
+                        const char *s,
+                        const char *end,
+                        const char **nextPtr)
+{
+  ENTITY *entity;
+  const char *textStart, *textEnd;
+  const char *next;
+  enum XML_Error result;
+  OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities;
+  if (!openEntity)
+    return XML_ERROR_UNEXPECTED_STATE;
+
+  entity = openEntity->entity;
+  textStart = ((char *)entity->textPtr) + entity->processed;
+  textEnd = (char *)(entity->textPtr + entity->textLen);
+
+#ifdef XML_DTD
+  if (entity->is_param) {
+    int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next);
+    result = doProlog(parser, internalEncoding, textStart, textEnd, tok,
+                      next, &next, XML_FALSE);
+  }
+  else
+#endif /* XML_DTD */
+    result = doContent(parser, openEntity->startTagLevel, internalEncoding,
+                       textStart, textEnd, &next, XML_FALSE);
+
+  if (result != XML_ERROR_NONE)
+    return result;
+  else if (textEnd != next && ps_parsing == XML_SUSPENDED) {
+    entity->processed = (int)(next - (char *)entity->textPtr);
+    return result;
+  }
+  else {
+    entity->open = XML_FALSE;
+    openInternalEntities = openEntity->next;
+    /* put openEntity back in list of free instances */
+    openEntity->next = freeInternalEntities;
+    freeInternalEntities = openEntity;
+  }
+
+#ifdef XML_DTD
+  if (entity->is_param) {
+    int tok;
+    processor = prologProcessor;
+    tok = XmlPrologTok(encoding, s, end, &next);
+    return doProlog(parser, encoding, s, end, tok, next, nextPtr,
+                    (XML_Bool)!ps_finalBuffer);
+  }
+  else
+#endif /* XML_DTD */
+  {
+    processor = contentProcessor;
+    /* see externalEntityContentProcessor vs contentProcessor */
+    return doContent(parser, parentParser ? 1 : 0, encoding, s, end,
+                     nextPtr, (XML_Bool)!ps_finalBuffer);
+  }
+}
+
+static enum XML_Error PTRCALL
+errorProcessor(XML_Parser parser,
+               const char *UNUSED_P(s),
+               const char *UNUSED_P(end),
+               const char **UNUSED_P(nextPtr))
+{
+  return errorCode;
+}
+
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+                    const char *ptr, const char *end,
+                    STRING_POOL *pool)
+{
+  enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr,
+                                               end, pool);
+  if (result)
+    return result;
+  if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
+    poolChop(pool);
+  if (!poolAppendChar(pool, XML_T('\0')))
+    return XML_ERROR_NO_MEMORY;
+  return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+                     const char *ptr, const char *end,
+                     STRING_POOL *pool)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  for (;;) {
+    const char *next;
+    int tok = XmlAttributeValueTok(enc, ptr, end, &next);
+    switch (tok) {
+    case XML_TOK_NONE:
+      return XML_ERROR_NONE;
+    case XML_TOK_INVALID:
+      if (enc == encoding)
+        eventPtr = next;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_PARTIAL:
+      if (enc == encoding)
+        eventPtr = ptr;
+      return XML_ERROR_INVALID_TOKEN;
+    case XML_TOK_CHAR_REF:
+      {
+        XML_Char buf[XML_ENCODE_MAX];
+        int i;
+        int n = XmlCharRefNumber(enc, ptr);
+        if (n < 0) {
+          if (enc == encoding)
+            eventPtr = ptr;
+          return XML_ERROR_BAD_CHAR_REF;
+        }
+        if (!isCdata
+            && n == 0x20 /* space */
+            && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+          break;
+        n = XmlEncode(n, (ICHAR *)buf);
+        if (!n) {
+          if (enc == encoding)
+            eventPtr = ptr;
+          return XML_ERROR_BAD_CHAR_REF;
+        }
+        for (i = 0; i < n; i++) {
+          if (!poolAppendChar(pool, buf[i]))
+            return XML_ERROR_NO_MEMORY;
+        }
+      }
+      break;
+    case XML_TOK_DATA_CHARS:
+      if (!poolAppend(pool, enc, ptr, next))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_TRAILING_CR:
+      next = ptr + enc->minBytesPerChar;
+      /* fall through */
+    case XML_TOK_ATTRIBUTE_VALUE_S:
+    case XML_TOK_DATA_NEWLINE:
+      if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+        break;
+      if (!poolAppendChar(pool, 0x20))
+        return XML_ERROR_NO_MEMORY;
+      break;
+    case XML_TOK_ENTITY_REF:
+      {
+        const XML_Char *name;
+        ENTITY *entity;
+        char checkEntityDecl;
+        XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc,
+                                              ptr + enc->minBytesPerChar,
+                                              next - enc->minBytesPerChar);
+        if (ch) {
+          if (!poolAppendChar(pool, ch))
+                return XML_ERROR_NO_MEMORY;
+          break;
+        }
+        name = poolStoreString(&temp2Pool, enc,
+                               ptr + enc->minBytesPerChar,
+                               next - enc->minBytesPerChar);
+        if (!name)
+          return XML_ERROR_NO_MEMORY;
+        entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0);
+        poolDiscard(&temp2Pool);
+        /* First, determine if a check for an existing declaration is needed;
+           if yes, check that the entity exists, and that it is internal.
+        */
+        if (pool == &dtd->pool)  /* are we called from prolog? */
+          checkEntityDecl =
+#ifdef XML_DTD
+              prologState.documentEntity &&
+#endif /* XML_DTD */
+              (dtd->standalone
+               ? !openInternalEntities
+               : !dtd->hasParamEntityRefs);
+        else /* if (pool == &tempPool): we are called from content */
+          checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone;
+        if (checkEntityDecl) {
+          if (!entity)
+            return XML_ERROR_UNDEFINED_ENTITY;
+          else if (!entity->is_internal)
+            return XML_ERROR_ENTITY_DECLARED_IN_PE;
+        }
+        else if (!entity) {
+          /* Cannot report skipped entity here - see comments on
+             skippedEntityHandler.
+          if (skippedEntityHandler)
+            skippedEntityHandler(handlerArg, name, 0);
+          */
+          /* Cannot call the default handler because this would be
+             out of sync with the call to the startElementHandler.
+          if ((pool == &tempPool) && defaultHandler)
+            reportDefault(parser, enc, ptr, next);
+          */
+          break;
+        }
+        if (entity->open) {
+          if (enc == encoding)
+            eventPtr = ptr;
+          return XML_ERROR_RECURSIVE_ENTITY_REF;
+        }
+        if (entity->notation) {
+          if (enc == encoding)
+            eventPtr = ptr;
+          return XML_ERROR_BINARY_ENTITY_REF;
+        }
+        if (!entity->textPtr) {
+          if (enc == encoding)
+            eventPtr = ptr;
+          return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
+        }
+        else {
+          enum XML_Error result;
+          const XML_Char *textEnd = entity->textPtr + entity->textLen;
+          entity->open = XML_TRUE;
+          result = appendAttributeValue(parser, internalEncoding, isCdata,
+                                        (char *)entity->textPtr,
+                                        (char *)textEnd, pool);
+          entity->open = XML_FALSE;
+          if (result)
+            return result;
+        }
+      }
+      break;
+    default:
+      if (enc == encoding)
+        eventPtr = ptr;
+      return XML_ERROR_UNEXPECTED_STATE;
+    }
+    ptr = next;
+  }
+  /* not reached */
+}
+
+static enum XML_Error
+storeEntityValue(XML_Parser parser,
+                 const ENCODING *enc,
+                 const char *entityTextPtr,
+                 const char *entityTextEnd)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  STRING_POOL *pool = &(dtd->entityValuePool);
+  enum XML_Error result = XML_ERROR_NONE;
+#ifdef XML_DTD
+  int oldInEntityValue = prologState.inEntityValue;
+  prologState.inEntityValue = 1;
+#endif /* XML_DTD */
+  /* never return Null for the value argument in EntityDeclHandler,
+     since this would indicate an external entity; therefore we
+     have to make sure that entityValuePool.start is not null */
+  if (!pool->blocks) {
+    if (!poolGrow(pool))
+      return XML_ERROR_NO_MEMORY;
+  }
+
+  for (;;) {
+    const char *next;
+    int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
+    switch (tok) {
+    case XML_TOK_PARAM_ENTITY_REF:
+#ifdef XML_DTD
+      if (isParamEntity || enc != encoding) {
+        const XML_Char *name;
+        ENTITY *entity;
+        name = poolStoreString(&tempPool, enc,
+                               entityTextPtr + enc->minBytesPerChar,
+                               next - enc->minBytesPerChar);
+        if (!name) {
+          result = XML_ERROR_NO_MEMORY;
+          goto endEntityValue;
+        }
+        entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0);
+        poolDiscard(&tempPool);
+        if (!entity) {
+          /* not a well-formedness error - see XML 1.0: WFC Entity Declared */
+          /* cannot report skipped entity here - see comments on
+             skippedEntityHandler
+          if (skippedEntityHandler)
+            skippedEntityHandler(handlerArg, name, 0);
+          */
+          dtd->keepProcessing = dtd->standalone;
+          goto endEntityValue;
+        }
+        if (entity->open) {
+          if (enc == encoding)
+            eventPtr = entityTextPtr;
+          result = XML_ERROR_RECURSIVE_ENTITY_REF;
+          goto endEntityValue;
+        }
+        if (entity->systemId) {
+          if (externalEntityRefHandler) {
+            dtd->paramEntityRead = XML_FALSE;
+            entity->open = XML_TRUE;
+            if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+                                          0,
+                                          entity->base,
+                                          entity->systemId,
+                                          entity->publicId)) {
+              entity->open = XML_FALSE;
+              result = XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+              goto endEntityValue;
+            }
+            entity->open = XML_FALSE;
+            if (!dtd->paramEntityRead)
+              dtd->keepProcessing = dtd->standalone;
+          }
+          else
+            dtd->keepProcessing = dtd->standalone;
+        }
+        else {
+          entity->open = XML_TRUE;
+          result = storeEntityValue(parser,
+                                    internalEncoding,
+                                    (char *)entity->textPtr,
+                                    (char *)(entity->textPtr
+                                             + entity->textLen));
+          entity->open = XML_FALSE;
+          if (result)
+            goto endEntityValue;
+        }
+        break;
+      }
+#endif /* XML_DTD */
+      /* In the internal subset, PE references are not legal
+         within markup declarations, e.g entity values in this case. */
+      eventPtr = entityTextPtr;
+      result = XML_ERROR_PARAM_ENTITY_REF;
+      goto endEntityValue;
+    case XML_TOK_NONE:
+      result = XML_ERROR_NONE;
+      goto endEntityValue;
+    case XML_TOK_ENTITY_REF:
+    case XML_TOK_DATA_CHARS:
+      if (!poolAppend(pool, enc, entityTextPtr, next)) {
+        result = XML_ERROR_NO_MEMORY;
+        goto endEntityValue;
+      }
+      break;
+    case XML_TOK_TRAILING_CR:
+      next = entityTextPtr + enc->minBytesPerChar;
+      /* fall through */
+    case XML_TOK_DATA_NEWLINE:
+      if (pool->end == pool->ptr && !poolGrow(pool)) {
+              result = XML_ERROR_NO_MEMORY;
+        goto endEntityValue;
+      }
+      *(pool->ptr)++ = 0xA;
+      break;
+    case XML_TOK_CHAR_REF:
+      {
+        XML_Char buf[XML_ENCODE_MAX];
+        int i;
+        int n = XmlCharRefNumber(enc, entityTextPtr);
+        if (n < 0) {
+          if (enc == encoding)
+            eventPtr = entityTextPtr;
+          result = XML_ERROR_BAD_CHAR_REF;
+          goto endEntityValue;
+        }
+        n = XmlEncode(n, (ICHAR *)buf);
+        if (!n) {
+          if (enc == encoding)
+            eventPtr = entityTextPtr;
+          result = XML_ERROR_BAD_CHAR_REF;
+          goto endEntityValue;
+        }
+        for (i = 0; i < n; i++) {
+          if (pool->end == pool->ptr && !poolGrow(pool)) {
+            result = XML_ERROR_NO_MEMORY;
+            goto endEntityValue;
+          }
+          *(pool->ptr)++ = buf[i];
+        }
+      }
+      break;
+    case XML_TOK_PARTIAL:
+      if (enc == encoding)
+        eventPtr = entityTextPtr;
+      result = XML_ERROR_INVALID_TOKEN;
+      goto endEntityValue;
+    case XML_TOK_INVALID:
+      if (enc == encoding)
+        eventPtr = next;
+      result = XML_ERROR_INVALID_TOKEN;
+      goto endEntityValue;
+    default:
+      if (enc == encoding)
+        eventPtr = entityTextPtr;
+      result = XML_ERROR_UNEXPECTED_STATE;
+      goto endEntityValue;
+    }
+    entityTextPtr = next;
+  }
+endEntityValue:
+#ifdef XML_DTD
+  prologState.inEntityValue = oldInEntityValue;
+#endif /* XML_DTD */
+  return result;
+}
+
+static void FASTCALL
+normalizeLines(XML_Char *s)
+{
+  XML_Char *p;
+  for (;; s++) {
+    if (*s == XML_T('\0'))
+      return;
+    if (*s == 0xD)
+      break;
+  }
+  p = s;
+  do {
+    if (*s == 0xD) {
+      *p++ = 0xA;
+      if (*++s == 0xA)
+        s++;
+    }
+    else
+      *p++ = *s++;
+  } while (*s);
+  *p = XML_T('\0');
+}
+
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
+                            const char *start, const char *end)
+{
+  const XML_Char *target;
+  XML_Char *data;
+  const char *tem;
+  if (!processingInstructionHandler) {
+    if (defaultHandler)
+      reportDefault(parser, enc, start, end);
+    return 1;
+  }
+  start += enc->minBytesPerChar * 2;
+  tem = start + XmlNameLength(enc, start);
+  target = poolStoreString(&tempPool, enc, start, tem);
+  if (!target)
+    return 0;
+  poolFinish(&tempPool);
+  data = poolStoreString(&tempPool, enc,
+                        XmlSkipS(enc, tem),
+                        end - enc->minBytesPerChar*2);
+  if (!data)
+    return 0;
+  normalizeLines(data);
+  processingInstructionHandler(handlerArg, target, data);
+  poolClear(&tempPool);
+  return 1;
+}
+
+static int
+reportComment(XML_Parser parser, const ENCODING *enc,
+              const char *start, const char *end)
+{
+  XML_Char *data;
+  if (!commentHandler) {
+    if (defaultHandler)
+      reportDefault(parser, enc, start, end);
+    return 1;
+  }
+  data = poolStoreString(&tempPool,
+                         enc,
+                         start + enc->minBytesPerChar * 4,
+                         end - enc->minBytesPerChar * 3);
+  if (!data)
+    return 0;
+  normalizeLines(data);
+  commentHandler(handlerArg, data);
+  poolClear(&tempPool);
+  return 1;
+}
+
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc,
+              const char *s, const char *end)
+{
+  if (MUST_CONVERT(enc, s)) {
+    enum XML_Convert_Result convert_res;
+    const char **eventPP;
+    const char **eventEndPP;
+    if (enc == encoding) {
+      eventPP = &eventPtr;
+      eventEndPP = &eventEndPtr;
+    }
+    else {
+      eventPP = &(openInternalEntities->internalEventPtr);
+      eventEndPP = &(openInternalEntities->internalEventEndPtr);
+    }
+    do {
+      ICHAR *dataPtr = (ICHAR *)dataBuf;
+      convert_res = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+      *eventEndPP = s;
+      defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf));
+      *eventPP = s;
+    } while ((convert_res != XML_CONVERT_COMPLETED) && (convert_res != XML_CONVERT_INPUT_INCOMPLETE));
+  }
+  else
+    defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s));
+}
+
+
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
+                XML_Bool isId, const XML_Char *value, XML_Parser parser)
+{
+  DEFAULT_ATTRIBUTE *att;
+  if (value || isId) {
+    /* The handling of default attributes gets messed up if we have
+       a default which duplicates a non-default. */
+    int i;
+    for (i = 0; i < type->nDefaultAtts; i++)
+      if (attId == type->defaultAtts[i].id)
+        return 1;
+    if (isId && !type->idAtt && !attId->xmlns)
+      type->idAtt = attId;
+  }
+  if (type->nDefaultAtts == type->allocDefaultAtts) {
+    if (type->allocDefaultAtts == 0) {
+      type->allocDefaultAtts = 8;
+      type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts
+                            * sizeof(DEFAULT_ATTRIBUTE));
+      if (!type->defaultAtts)
+        return 0;
+    }
+    else {
+      DEFAULT_ATTRIBUTE *temp;
+      int count = type->allocDefaultAtts * 2;
+      temp = (DEFAULT_ATTRIBUTE *)
+        REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE)));
+      if (temp == NULL)
+        return 0;
+      type->allocDefaultAtts = count;
+      type->defaultAtts = temp;
+    }
+  }
+  att = type->defaultAtts + type->nDefaultAtts;
+  att->id = attId;
+  att->value = value;
+  att->isCdata = isCdata;
+  if (!isCdata)
+    attId->maybeTokenized = XML_TRUE;
+  type->nDefaultAtts += 1;
+  return 1;
+}
+
+static int
+setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  const XML_Char *name;
+  for (name = elementType->name; *name; name++) {
+    if (*name == XML_T(ASCII_COLON)) {
+      PREFIX *prefix;
+      const XML_Char *s;
+      for (s = elementType->name; s != name; s++) {
+        if (!poolAppendChar(&dtd->pool, *s))
+          return 0;
+      }
+      if (!poolAppendChar(&dtd->pool, XML_T('\0')))
+        return 0;
+      prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool),
+                                sizeof(PREFIX));
+      if (!prefix)
+        return 0;
+      if (prefix->name == poolStart(&dtd->pool))
+        poolFinish(&dtd->pool);
+      else
+        poolDiscard(&dtd->pool);
+      elementType->prefix = prefix;
+
+    }
+  }
+  return 1;
+}
+
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc,
+               const char *start, const char *end)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  ATTRIBUTE_ID *id;
+  const XML_Char *name;
+  if (!poolAppendChar(&dtd->pool, XML_T('\0')))
+    return NULL;
+  name = poolStoreString(&dtd->pool, enc, start, end);
+  if (!name)
+    return NULL;
+  /* skip quotation mark - its storage will be re-used (like in name[-1]) */
+  ++name;
+  id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID));
+  if (!id)
+    return NULL;
+  if (id->name != name)
+    poolDiscard(&dtd->pool);
+  else {
+    poolFinish(&dtd->pool);
+    if (!ns)
+      ;
+    else if (name[0] == XML_T(ASCII_x)
+        && name[1] == XML_T(ASCII_m)
+        && name[2] == XML_T(ASCII_l)
+        && name[3] == XML_T(ASCII_n)
+        && name[4] == XML_T(ASCII_s)
+        && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) {
+      if (name[5] == XML_T('\0'))
+        id->prefix = &dtd->defaultPrefix;
+      else
+        id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX));
+      id->xmlns = XML_TRUE;
+    }
+    else {
+      int i;
+      for (i = 0; name[i]; i++) {
+        /* attributes without prefix are *not* in the default namespace */
+        if (name[i] == XML_T(ASCII_COLON)) {
+          int j;
+          for (j = 0; j < i; j++) {
+            if (!poolAppendChar(&dtd->pool, name[j]))
+              return NULL;
+          }
+          if (!poolAppendChar(&dtd->pool, XML_T('\0')))
+            return NULL;
+          id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool),
+                                        sizeof(PREFIX));
+          if (!id->prefix)
+            return NULL;
+          if (id->prefix->name == poolStart(&dtd->pool))
+            poolFinish(&dtd->pool);
+          else
+            poolDiscard(&dtd->pool);
+          break;
+        }
+      }
+    }
+  }
+  return id;
+}
+
+#define CONTEXT_SEP XML_T(ASCII_FF)
+
+static const XML_Char *
+getContext(XML_Parser parser)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  HASH_TABLE_ITER iter;
+  XML_Bool needSep = XML_FALSE;
+
+  if (dtd->defaultPrefix.binding) {
+    int i;
+    int len;
+    if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS)))
+      return NULL;
+    len = dtd->defaultPrefix.binding->uriLen;
+    if (namespaceSeparator)
+      len--;
+    for (i = 0; i < len; i++)
+      if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i]))
+        return NULL;
+    needSep = XML_TRUE;
+  }
+
+  hashTableIterInit(&iter, &(dtd->prefixes));
+  for (;;) {
+    int i;
+    int len;
+    const XML_Char *s;
+    PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter);
+    if (!prefix)
+      break;
+    if (!prefix->binding)
+      continue;
+    if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+      return NULL;
+    for (s = prefix->name; *s; s++)
+      if (!poolAppendChar(&tempPool, *s))
+        return NULL;
+    if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS)))
+      return NULL;
+    len = prefix->binding->uriLen;
+    if (namespaceSeparator)
+      len--;
+    for (i = 0; i < len; i++)
+      if (!poolAppendChar(&tempPool, prefix->binding->uri[i]))
+        return NULL;
+    needSep = XML_TRUE;
+  }
+
+
+  hashTableIterInit(&iter, &(dtd->generalEntities));
+  for (;;) {
+    const XML_Char *s;
+    ENTITY *e = (ENTITY *)hashTableIterNext(&iter);
+    if (!e)
+      break;
+    if (!e->open)
+      continue;
+    if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+      return NULL;
+    for (s = e->name; *s; s++)
+      if (!poolAppendChar(&tempPool, *s))
+        return 0;
+    needSep = XML_TRUE;
+  }
+
+  if (!poolAppendChar(&tempPool, XML_T('\0')))
+    return NULL;
+  return tempPool.start;
+}
+
+static XML_Bool
+setContext(XML_Parser parser, const XML_Char *context)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  const XML_Char *s = context;
+
+  while (*context != XML_T('\0')) {
+    if (*s == CONTEXT_SEP || *s == XML_T('\0')) {
+      ENTITY *e;
+      if (!poolAppendChar(&tempPool, XML_T('\0')))
+        return XML_FALSE;
+      e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0);
+      if (e)
+        e->open = XML_TRUE;
+      if (*s != XML_T('\0'))
+        s++;
+      context = s;
+      poolDiscard(&tempPool);
+    }
+    else if (*s == XML_T(ASCII_EQUALS)) {
+      PREFIX *prefix;
+      if (poolLength(&tempPool) == 0)
+        prefix = &dtd->defaultPrefix;
+      else {
+        if (!poolAppendChar(&tempPool, XML_T('\0')))
+          return XML_FALSE;
+        prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool),
+                                  sizeof(PREFIX));
+        if (!prefix)
+          return XML_FALSE;
+        if (prefix->name == poolStart(&tempPool)) {
+          prefix->name = poolCopyString(&dtd->pool, prefix->name);
+          if (!prefix->name)
+            return XML_FALSE;
+        }
+        poolDiscard(&tempPool);
+      }
+      for (context = s + 1;
+           *context != CONTEXT_SEP && *context != XML_T('\0');
+           context++)
+        if (!poolAppendChar(&tempPool, *context))
+          return XML_FALSE;
+      if (!poolAppendChar(&tempPool, XML_T('\0')))
+        return XML_FALSE;
+      if (addBinding(parser, prefix, NULL, poolStart(&tempPool),
+                     &inheritedBindings) != XML_ERROR_NONE)
+        return XML_FALSE;
+      poolDiscard(&tempPool);
+      if (*context != XML_T('\0'))
+        ++context;
+      s = context;
+    }
+    else {
+      if (!poolAppendChar(&tempPool, *s))
+        return XML_FALSE;
+      s++;
+    }
+  }
+  return XML_TRUE;
+}
+
+static void FASTCALL
+normalizePublicId(XML_Char *publicId)
+{
+  XML_Char *p = publicId;
+  XML_Char *s;
+  for (s = publicId; *s; s++) {
+    switch (*s) {
+    case 0x20:
+    case 0xD:
+    case 0xA:
+      if (p != publicId && p[-1] != 0x20)
+        *p++ = 0x20;
+      break;
+    default:
+      *p++ = *s;
+    }
+  }
+  if (p != publicId && p[-1] == 0x20)
+    --p;
+  *p = XML_T('\0');
+}
+
+static DTD *
+dtdCreate(const XML_Memory_Handling_Suite *ms)
+{
+  DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD));
+  if (p == NULL)
+    return p;
+  poolInit(&(p->pool), ms);
+  poolInit(&(p->entityValuePool), ms);
+  hashTableInit(&(p->generalEntities), ms);
+  hashTableInit(&(p->elementTypes), ms);
+  hashTableInit(&(p->attributeIds), ms);
+  hashTableInit(&(p->prefixes), ms);
+#ifdef XML_DTD
+  p->paramEntityRead = XML_FALSE;
+  hashTableInit(&(p->paramEntities), ms);
+#endif /* XML_DTD */
+  p->defaultPrefix.name = NULL;
+  p->defaultPrefix.binding = NULL;
+
+  p->in_eldecl = XML_FALSE;
+  p->scaffIndex = NULL;
+  p->scaffold = NULL;
+  p->scaffLevel = 0;
+  p->scaffSize = 0;
+  p->scaffCount = 0;
+  p->contentStringLen = 0;
+
+  p->keepProcessing = XML_TRUE;
+  p->hasParamEntityRefs = XML_FALSE;
+  p->standalone = XML_FALSE;
+  return p;
+}
+
+static void
+dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms)
+{
+  HASH_TABLE_ITER iter;
+  hashTableIterInit(&iter, &(p->elementTypes));
+  for (;;) {
+    ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+    if (!e)
+      break;
+    if (e->allocDefaultAtts != 0)
+      ms->free_fcn(e->defaultAtts);
+  }
+  hashTableClear(&(p->generalEntities));
+#ifdef XML_DTD
+  p->paramEntityRead = XML_FALSE;
+  hashTableClear(&(p->paramEntities));
+#endif /* XML_DTD */
+  hashTableClear(&(p->elementTypes));
+  hashTableClear(&(p->attributeIds));
+  hashTableClear(&(p->prefixes));
+  poolClear(&(p->pool));
+  poolClear(&(p->entityValuePool));
+  p->defaultPrefix.name = NULL;
+  p->defaultPrefix.binding = NULL;
+
+  p->in_eldecl = XML_FALSE;
+
+  ms->free_fcn(p->scaffIndex);
+  p->scaffIndex = NULL;
+  ms->free_fcn(p->scaffold);
+  p->scaffold = NULL;
+
+  p->scaffLevel = 0;
+  p->scaffSize = 0;
+  p->scaffCount = 0;
+  p->contentStringLen = 0;
+
+  p->keepProcessing = XML_TRUE;
+  p->hasParamEntityRefs = XML_FALSE;
+  p->standalone = XML_FALSE;
+}
+
+static void
+dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms)
+{
+  HASH_TABLE_ITER iter;
+  hashTableIterInit(&iter, &(p->elementTypes));
+  for (;;) {
+    ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+    if (!e)
+      break;
+    if (e->allocDefaultAtts != 0)
+      ms->free_fcn(e->defaultAtts);
+  }
+  hashTableDestroy(&(p->generalEntities));
+#ifdef XML_DTD
+  hashTableDestroy(&(p->paramEntities));
+#endif /* XML_DTD */
+  hashTableDestroy(&(p->elementTypes));
+  hashTableDestroy(&(p->attributeIds));
+  hashTableDestroy(&(p->prefixes));
+  poolDestroy(&(p->pool));
+  poolDestroy(&(p->entityValuePool));
+  if (isDocEntity) {
+    ms->free_fcn(p->scaffIndex);
+    ms->free_fcn(p->scaffold);
+  }
+  ms->free_fcn(p);
+}
+
+/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise.
+   The new DTD has already been initialized.
+*/
+static int
+dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms)
+{
+  HASH_TABLE_ITER iter;
+
+  /* Copy the prefix table. */
+
+  hashTableIterInit(&iter, &(oldDtd->prefixes));
+  for (;;) {
+    const XML_Char *name;
+    const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter);
+    if (!oldP)
+      break;
+    name = poolCopyString(&(newDtd->pool), oldP->name);
+    if (!name)
+      return 0;
+    if (!lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX)))
+      return 0;
+  }
+
+  hashTableIterInit(&iter, &(oldDtd->attributeIds));
+
+  /* Copy the attribute id table. */
+
+  for (;;) {
+    ATTRIBUTE_ID *newA;
+    const XML_Char *name;
+    const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter);
+
+    if (!oldA)
+      break;
+    /* Remember to allocate the scratch byte before the name. */
+    if (!poolAppendChar(&(newDtd->pool), XML_T('\0')))
+      return 0;
+    name = poolCopyString(&(newDtd->pool), oldA->name);
+    if (!name)
+      return 0;
+    ++name;
+    newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name,
+                                  sizeof(ATTRIBUTE_ID));
+    if (!newA)
+      return 0;
+    newA->maybeTokenized = oldA->maybeTokenized;
+    if (oldA->prefix) {
+      newA->xmlns = oldA->xmlns;
+      if (oldA->prefix == &oldDtd->defaultPrefix)
+        newA->prefix = &newDtd->defaultPrefix;
+      else
+        newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes),
+                                        oldA->prefix->name, 0);
+    }
+  }
+
+  /* Copy the element type table. */
+
+  hashTableIterInit(&iter, &(oldDtd->elementTypes));
+
+  for (;;) {
+    int i;
+    ELEMENT_TYPE *newE;
+    const XML_Char *name;
+    const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+    if (!oldE)
+      break;
+    name = poolCopyString(&(newDtd->pool), oldE->name);
+    if (!name)
+      return 0;
+    newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name,
+                                  sizeof(ELEMENT_TYPE));
+    if (!newE)
+      return 0;
+    if (oldE->nDefaultAtts) {
+      newE->defaultAtts = (DEFAULT_ATTRIBUTE *)
+          ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+      if (!newE->defaultAtts) {
+        ms->free_fcn(newE);
+        return 0;
+      }
+    }
+    if (oldE->idAtt)
+      newE->idAtt = (ATTRIBUTE_ID *)
+          lookup(oldParser, &(newDtd->attributeIds), oldE->idAtt->name, 0);
+    newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts;
+    if (oldE->prefix)
+      newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes),
+                                      oldE->prefix->name, 0);
+    for (i = 0; i < newE->nDefaultAtts; i++) {
+      newE->defaultAtts[i].id = (ATTRIBUTE_ID *)
+          lookup(oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0);
+      newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata;
+      if (oldE->defaultAtts[i].value) {
+        newE->defaultAtts[i].value
+            = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value);
+        if (!newE->defaultAtts[i].value)
+          return 0;
+      }
+      else
+        newE->defaultAtts[i].value = NULL;
+    }
+  }
+
+  /* Copy the entity tables. */
+  if (!copyEntityTable(oldParser,
+                       &(newDtd->generalEntities),
+                       &(newDtd->pool),
+                       &(oldDtd->generalEntities)))
+      return 0;
+
+#ifdef XML_DTD
+  if (!copyEntityTable(oldParser,
+                       &(newDtd->paramEntities),
+                       &(newDtd->pool),
+                       &(oldDtd->paramEntities)))
+      return 0;
+  newDtd->paramEntityRead = oldDtd->paramEntityRead;
+#endif /* XML_DTD */
+
+  newDtd->keepProcessing = oldDtd->keepProcessing;
+  newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs;
+  newDtd->standalone = oldDtd->standalone;
+
+  /* Don't want deep copying for scaffolding */
+  newDtd->in_eldecl = oldDtd->in_eldecl;
+  newDtd->scaffold = oldDtd->scaffold;
+  newDtd->contentStringLen = oldDtd->contentStringLen;
+  newDtd->scaffSize = oldDtd->scaffSize;
+  newDtd->scaffLevel = oldDtd->scaffLevel;
+  newDtd->scaffIndex = oldDtd->scaffIndex;
+
+  return 1;
+}  /* End dtdCopy */
+
+static int
+copyEntityTable(XML_Parser oldParser,
+                HASH_TABLE *newTable,
+                STRING_POOL *newPool,
+                const HASH_TABLE *oldTable)
+{
+  HASH_TABLE_ITER iter;
+  const XML_Char *cachedOldBase = NULL;
+  const XML_Char *cachedNewBase = NULL;
+
+  hashTableIterInit(&iter, oldTable);
+
+  for (;;) {
+    ENTITY *newE;
+    const XML_Char *name;
+    const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter);
+    if (!oldE)
+      break;
+    name = poolCopyString(newPool, oldE->name);
+    if (!name)
+      return 0;
+    newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY));
+    if (!newE)
+      return 0;
+    if (oldE->systemId) {
+      const XML_Char *tem = poolCopyString(newPool, oldE->systemId);
+      if (!tem)
+        return 0;
+      newE->systemId = tem;
+      if (oldE->base) {
+        if (oldE->base == cachedOldBase)
+          newE->base = cachedNewBase;
+        else {
+          cachedOldBase = oldE->base;
+          tem = poolCopyString(newPool, cachedOldBase);
+          if (!tem)
+            return 0;
+          cachedNewBase = newE->base = tem;
+        }
+      }
+      if (oldE->publicId) {
+        tem = poolCopyString(newPool, oldE->publicId);
+        if (!tem)
+          return 0;
+        newE->publicId = tem;
+      }
+    }
+    else {
+      const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr,
+                                            oldE->textLen);
+      if (!tem)
+        return 0;
+      newE->textPtr = tem;
+      newE->textLen = oldE->textLen;
+    }
+    if (oldE->notation) {
+      const XML_Char *tem = poolCopyString(newPool, oldE->notation);
+      if (!tem)
+        return 0;
+      newE->notation = tem;
+    }
+    newE->is_param = oldE->is_param;
+    newE->is_internal = oldE->is_internal;
+  }
+  return 1;
+}
+
+#define INIT_POWER 6
+
+static XML_Bool FASTCALL
+keyeq(KEY s1, KEY s2)
+{
+  for (; *s1 == *s2; s1++, s2++)
+    if (*s1 == 0)
+      return XML_TRUE;
+  return XML_FALSE;
+}
+
+static unsigned long FASTCALL
+hash(XML_Parser parser, KEY s)
+{
+  unsigned long h = hash_secret_salt;
+  while (*s)
+    h = CHAR_HASH(h, *s++);
+  return h;
+}
+
+static NAMED *
+lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize)
+{
+  size_t i;
+  if (table->size == 0) {
+    size_t tsize;
+    if (!createSize)
+      return NULL;
+    table->power = INIT_POWER;
+    /* table->size is a power of 2 */
+    table->size = (size_t)1 << INIT_POWER;
+    tsize = table->size * sizeof(NAMED *);
+    table->v = (NAMED **)table->mem->malloc_fcn(tsize);
+    if (!table->v) {
+      table->size = 0;
+      return NULL;
+    }
+    memset(table->v, 0, tsize);
+    i = hash(parser, name) & ((unsigned long)table->size - 1);
+  }
+  else {
+    unsigned long h = hash(parser, name);
+    unsigned long mask = (unsigned long)table->size - 1;
+    unsigned char step = 0;
+    i = h & mask;
+    while (table->v[i]) {
+      if (keyeq(name, table->v[i]->name))
+        return table->v[i];
+      if (!step)
+        step = PROBE_STEP(h, mask, table->power);
+      i < step ? (i += table->size - step) : (i -= step);
+    }
+    if (!createSize)
+      return NULL;
+
+    /* check for overflow (table is half full) */
+    if (table->used >> (table->power - 1)) {
+      unsigned char newPower = table->power + 1;
+      size_t newSize = (size_t)1 << newPower;
+      unsigned long newMask = (unsigned long)newSize - 1;
+      size_t tsize = newSize * sizeof(NAMED *);
+      NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
+      if (!newV)
+        return NULL;
+      memset(newV, 0, tsize);
+      for (i = 0; i < table->size; i++)
+        if (table->v[i]) {
+          unsigned long newHash = hash(parser, table->v[i]->name);
+          size_t j = newHash & newMask;
+          step = 0;
+          while (newV[j]) {
+            if (!step)
+              step = PROBE_STEP(newHash, newMask, newPower);
+            j < step ? (j += newSize - step) : (j -= step);
+          }
+          newV[j] = table->v[i];
+        }
+      table->mem->free_fcn(table->v);
+      table->v = newV;
+      table->power = newPower;
+      table->size = newSize;
+      i = h & newMask;
+      step = 0;
+      while (table->v[i]) {
+        if (!step)
+          step = PROBE_STEP(h, newMask, newPower);
+        i < step ? (i += newSize - step) : (i -= step);
+      }
+    }
+  }
+  table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize);
+  if (!table->v[i])
+    return NULL;
+  memset(table->v[i], 0, createSize);
+  table->v[i]->name = name;
+  (table->used)++;
+  return table->v[i];
+}
+
+static void FASTCALL
+hashTableClear(HASH_TABLE *table)
+{
+  size_t i;
+  for (i = 0; i < table->size; i++) {
+    table->mem->free_fcn(table->v[i]);
+    table->v[i] = NULL;
+  }
+  table->used = 0;
+}
+
+static void FASTCALL
+hashTableDestroy(HASH_TABLE *table)
+{
+  size_t i;
+  for (i = 0; i < table->size; i++)
+    table->mem->free_fcn(table->v[i]);
+  table->mem->free_fcn(table->v);
+}
+
+static void FASTCALL
+hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms)
+{
+  p->power = 0;
+  p->size = 0;
+  p->used = 0;
+  p->v = NULL;
+  p->mem = ms;
+}
+
+static void FASTCALL
+hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table)
+{
+  iter->p = table->v;
+  iter->end = iter->p + table->size;
+}
+
+static NAMED * FASTCALL
+hashTableIterNext(HASH_TABLE_ITER *iter)
+{
+  while (iter->p != iter->end) {
+    NAMED *tem = *(iter->p)++;
+    if (tem)
+      return tem;
+  }
+  return NULL;
+}
+
+static void FASTCALL
+poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms)
+{
+  pool->blocks = NULL;
+  pool->freeBlocks = NULL;
+  pool->start = NULL;
+  pool->ptr = NULL;
+  pool->end = NULL;
+  pool->mem = ms;
+}
+
+static void FASTCALL
+poolClear(STRING_POOL *pool)
+{
+  if (!pool->freeBlocks)
+    pool->freeBlocks = pool->blocks;
+  else {
+    BLOCK *p = pool->blocks;
+    while (p) {
+      BLOCK *tem = p->next;
+      p->next = pool->freeBlocks;
+      pool->freeBlocks = p;
+      p = tem;
+    }
+  }
+  pool->blocks = NULL;
+  pool->start = NULL;
+  pool->ptr = NULL;
+  pool->end = NULL;
+}
+
+static void FASTCALL
+poolDestroy(STRING_POOL *pool)
+{
+  BLOCK *p = pool->blocks;
+  while (p) {
+    BLOCK *tem = p->next;
+    pool->mem->free_fcn(p);
+    p = tem;
+  }
+  p = pool->freeBlocks;
+  while (p) {
+    BLOCK *tem = p->next;
+    pool->mem->free_fcn(p);
+    p = tem;
+  }
+}
+
+static XML_Char *
+poolAppend(STRING_POOL *pool, const ENCODING *enc,
+           const char *ptr, const char *end)
+{
+  if (!pool->ptr && !poolGrow(pool))
+    return NULL;
+  for (;;) {
+    const enum XML_Convert_Result convert_res = XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end);
+    if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
+      break;
+    if (!poolGrow(pool))
+      return NULL;
+  }
+  return pool->start;
+}
+
+static const XML_Char * FASTCALL
+poolCopyString(STRING_POOL *pool, const XML_Char *s)
+{
+  do {
+    if (!poolAppendChar(pool, *s))
+      return NULL;
+  } while (*s++);
+  s = pool->start;
+  poolFinish(pool);
+  return s;
+}
+
+static const XML_Char *
+poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n)
+{
+  if (!pool->ptr && !poolGrow(pool))
+    return NULL;
+  for (; n > 0; --n, s++) {
+    if (!poolAppendChar(pool, *s))
+      return NULL;
+  }
+  s = pool->start;
+  poolFinish(pool);
+  return s;
+}
+
+static const XML_Char * FASTCALL
+poolAppendString(STRING_POOL *pool, const XML_Char *s)
+{
+  while (*s) {
+    if (!poolAppendChar(pool, *s))
+      return NULL;
+    s++;
+  }
+  return pool->start;
+}
+
+static XML_Char *
+poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+                const char *ptr, const char *end)
+{
+  if (!poolAppend(pool, enc, ptr, end))
+    return NULL;
+  if (pool->ptr == pool->end && !poolGrow(pool))
+    return NULL;
+  *(pool->ptr)++ = 0;
+  return pool->start;
+}
+
+static XML_Bool FASTCALL
+poolGrow(STRING_POOL *pool)
+{
+  if (pool->freeBlocks) {
+    if (pool->start == 0) {
+      pool->blocks = pool->freeBlocks;
+      pool->freeBlocks = pool->freeBlocks->next;
+      pool->blocks->next = NULL;
+      pool->start = pool->blocks->s;
+      pool->end = pool->start + pool->blocks->size;
+      pool->ptr = pool->start;
+      return XML_TRUE;
+    }
+    if (pool->end - pool->start < pool->freeBlocks->size) {
+      BLOCK *tem = pool->freeBlocks->next;
+      pool->freeBlocks->next = pool->blocks;
+      pool->blocks = pool->freeBlocks;
+      pool->freeBlocks = tem;
+      memcpy(pool->blocks->s, pool->start,
+             (pool->end - pool->start) * sizeof(XML_Char));
+      pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+      pool->start = pool->blocks->s;
+      pool->end = pool->start + pool->blocks->size;
+      return XML_TRUE;
+    }
+  }
+  if (pool->blocks && pool->start == pool->blocks->s) {
+    BLOCK *temp;
+    int blockSize = (int)((unsigned)(pool->end - pool->start)*2U);
+
+    if (blockSize < 0)
+      return XML_FALSE;
+
+    temp = (BLOCK *)
+      pool->mem->realloc_fcn(pool->blocks,
+                             (offsetof(BLOCK, s)
+                              + blockSize * sizeof(XML_Char)));
+    if (temp == NULL)
+      return XML_FALSE;
+    pool->blocks = temp;
+    pool->blocks->size = blockSize;
+    pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+    pool->start = pool->blocks->s;
+    pool->end = pool->start + blockSize;
+  }
+  else {
+    BLOCK *tem;
+    int blockSize = (int)(pool->end - pool->start);
+
+    if (blockSize < 0)
+      return XML_FALSE;
+
+    if (blockSize < INIT_BLOCK_SIZE)
+      blockSize = INIT_BLOCK_SIZE;
+    else
+      blockSize *= 2;
+    tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s)
+                                        + blockSize * sizeof(XML_Char));
+    if (!tem)
+      return XML_FALSE;
+    tem->size = blockSize;
+    tem->next = pool->blocks;
+    pool->blocks = tem;
+    if (pool->ptr != pool->start)
+      memcpy(tem->s, pool->start,
+             (pool->ptr - pool->start) * sizeof(XML_Char));
+    pool->ptr = tem->s + (pool->ptr - pool->start);
+    pool->start = tem->s;
+    pool->end = tem->s + blockSize;
+  }
+  return XML_TRUE;
+}
+
+static int FASTCALL
+nextScaffoldPart(XML_Parser parser)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  CONTENT_SCAFFOLD * me;
+  int next;
+
+  if (!dtd->scaffIndex) {
+    dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int));
+    if (!dtd->scaffIndex)
+      return -1;
+    dtd->scaffIndex[0] = 0;
+  }
+
+  if (dtd->scaffCount >= dtd->scaffSize) {
+    CONTENT_SCAFFOLD *temp;
+    if (dtd->scaffold) {
+      temp = (CONTENT_SCAFFOLD *)
+        REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
+      if (temp == NULL)
+        return -1;
+      dtd->scaffSize *= 2;
+    }
+    else {
+      temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS
+                                        * sizeof(CONTENT_SCAFFOLD));
+      if (temp == NULL)
+        return -1;
+      dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS;
+    }
+    dtd->scaffold = temp;
+  }
+  next = dtd->scaffCount++;
+  me = &dtd->scaffold[next];
+  if (dtd->scaffLevel) {
+    CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]];
+    if (parent->lastchild) {
+      dtd->scaffold[parent->lastchild].nextsib = next;
+    }
+    if (!parent->childcnt)
+      parent->firstchild = next;
+    parent->lastchild = next;
+    parent->childcnt++;
+  }
+  me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0;
+  return next;
+}
+
+static void
+build_node(XML_Parser parser,
+           int src_node,
+           XML_Content *dest,
+           XML_Content **contpos,
+           XML_Char **strpos)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  dest->type = dtd->scaffold[src_node].type;
+  dest->quant = dtd->scaffold[src_node].quant;
+  if (dest->type == XML_CTYPE_NAME) {
+    const XML_Char *src;
+    dest->name = *strpos;
+    src = dtd->scaffold[src_node].name;
+    for (;;) {
+      *(*strpos)++ = *src;
+      if (!*src)
+        break;
+      src++;
+    }
+    dest->numchildren = 0;
+    dest->children = NULL;
+  }
+  else {
+    unsigned int i;
+    int cn;
+    dest->numchildren = dtd->scaffold[src_node].childcnt;
+    dest->children = *contpos;
+    *contpos += dest->numchildren;
+    for (i = 0, cn = dtd->scaffold[src_node].firstchild;
+         i < dest->numchildren;
+         i++, cn = dtd->scaffold[cn].nextsib) {
+      build_node(parser, cn, &(dest->children[i]), contpos, strpos);
+    }
+    dest->name = NULL;
+  }
+}
+
+static XML_Content *
+build_model (XML_Parser parser)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  XML_Content *ret;
+  XML_Content *cpos;
+  XML_Char * str;
+  int allocsize = (dtd->scaffCount * sizeof(XML_Content)
+                   + (dtd->contentStringLen * sizeof(XML_Char)));
+
+  ret = (XML_Content *)MALLOC(allocsize);
+  if (!ret)
+    return NULL;
+
+  str =  (XML_Char *) (&ret[dtd->scaffCount]);
+  cpos = &ret[1];
+
+  build_node(parser, 0, ret, &cpos, &str);
+  return ret;
+}
+
+static ELEMENT_TYPE *
+getElementType(XML_Parser parser,
+               const ENCODING *enc,
+               const char *ptr,
+               const char *end)
+{
+  DTD * const dtd = _dtd;  /* save one level of indirection */
+  const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end);
+  ELEMENT_TYPE *ret;
+
+  if (!name)
+    return NULL;
+  ret = (ELEMENT_TYPE *) lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE));
+  if (!ret)
+    return NULL;
+  if (ret->name != name)
+    poolDiscard(&dtd->pool);
+  else {
+    poolFinish(&dtd->pool);
+    if (!setElementTypePrefix(parser, ret))
+      return NULL;
+  }
+  return ret;
+}
diff --git a/deps/EXPAT/expat/xmlrole.c b/deps/EXPAT/expat/xmlrole.c
new file mode 100644
index 000000000..8475238d3
--- /dev/null
+++ b/deps/EXPAT/expat/xmlrole.c
@@ -0,0 +1,1322 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+#include <stddef.h>
+#include "expat_config.h"
+#include "expat_external.h"
+#include "internal.h"
+#include "xmlrole.h"
+#include "ascii.h"
+
+/* Doesn't check:
+
+ that ,| are not mixed in a model group
+ content of literals
+
+*/
+
+static const char KW_ANY[] = {
+    ASCII_A, ASCII_N, ASCII_Y, '\0' };
+static const char KW_ATTLIST[] = {
+    ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' };
+static const char KW_CDATA[] = {
+    ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_DOCTYPE[] = {
+    ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' };
+static const char KW_ELEMENT[] = {
+    ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' };
+static const char KW_EMPTY[] = {
+    ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' };
+static const char KW_ENTITIES[] = {
+    ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S,
+    '\0' };
+static const char KW_ENTITY[] = {
+    ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' };
+static const char KW_FIXED[] = {
+    ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' };
+static const char KW_ID[] = {
+    ASCII_I, ASCII_D, '\0' };
+static const char KW_IDREF[] = {
+    ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' };
+static const char KW_IDREFS[] = {
+    ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' };
+#ifdef XML_DTD
+static const char KW_IGNORE[] = {
+    ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' };
+#endif
+static const char KW_IMPLIED[] = {
+    ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' };
+#ifdef XML_DTD
+static const char KW_INCLUDE[] = {
+    ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' };
+#endif
+static const char KW_NDATA[] = {
+    ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_NMTOKEN[] = {
+    ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' };
+static const char KW_NMTOKENS[] = {
+    ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S,
+    '\0' };
+static const char KW_NOTATION[] =
+    { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N,
+      '\0' };
+static const char KW_PCDATA[] = {
+    ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_PUBLIC[] = {
+    ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' };
+static const char KW_REQUIRED[] = {
+    ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D,
+    '\0' };
+static const char KW_SYSTEM[] = {
+    ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' };
+
+#ifndef MIN_BYTES_PER_CHAR
+#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar)
+#endif
+
+#ifdef XML_DTD
+#define setTopLevel(state) \
+  ((state)->handler = ((state)->documentEntity \
+                       ? internalSubset \
+                       : externalSubset1))
+#else /* not XML_DTD */
+#define setTopLevel(state) ((state)->handler = internalSubset)
+#endif /* not XML_DTD */
+
+typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state,
+                                   int tok,
+                                   const char *ptr,
+                                   const char *end,
+                                   const ENCODING *enc);
+
+static PROLOG_HANDLER
+  prolog0, prolog1, prolog2,
+  doctype0, doctype1, doctype2, doctype3, doctype4, doctype5,
+  internalSubset,
+  entity0, entity1, entity2, entity3, entity4, entity5, entity6,
+  entity7, entity8, entity9, entity10,
+  notation0, notation1, notation2, notation3, notation4,
+  attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6,
+  attlist7, attlist8, attlist9,
+  element0, element1, element2, element3, element4, element5, element6,
+  element7,
+#ifdef XML_DTD
+  externalSubset0, externalSubset1,
+  condSect0, condSect1, condSect2,
+#endif /* XML_DTD */
+  declClose,
+  error;
+
+static int FASTCALL common(PROLOG_STATE *state, int tok);
+
+static int PTRCALL
+prolog0(PROLOG_STATE *state,
+        int tok,
+        const char *ptr,
+        const char *end,
+        const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    state->handler = prolog1;
+    return XML_ROLE_NONE;
+  case XML_TOK_XML_DECL:
+    state->handler = prolog1;
+    return XML_ROLE_XML_DECL;
+  case XML_TOK_PI:
+    state->handler = prolog1;
+    return XML_ROLE_PI;
+  case XML_TOK_COMMENT:
+    state->handler = prolog1;
+    return XML_ROLE_COMMENT;
+  case XML_TOK_BOM:
+    return XML_ROLE_NONE;
+  case XML_TOK_DECL_OPEN:
+    if (!XmlNameMatchesAscii(enc,
+                             ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                             end,
+                             KW_DOCTYPE))
+      break;
+    state->handler = doctype0;
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_INSTANCE_START:
+    state->handler = error;
+    return XML_ROLE_INSTANCE_START;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+prolog1(PROLOG_STATE *state,
+        int tok,
+        const char *ptr,
+        const char *end,
+        const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_PI:
+    return XML_ROLE_PI;
+  case XML_TOK_COMMENT:
+    return XML_ROLE_COMMENT;
+  case XML_TOK_BOM:
+    return XML_ROLE_NONE;
+  case XML_TOK_DECL_OPEN:
+    if (!XmlNameMatchesAscii(enc,
+                             ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                             end,
+                             KW_DOCTYPE))
+      break;
+    state->handler = doctype0;
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_INSTANCE_START:
+    state->handler = error;
+    return XML_ROLE_INSTANCE_START;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+prolog2(PROLOG_STATE *state,
+        int tok,
+        const char *UNUSED_P(ptr),
+        const char *UNUSED_P(end),
+        const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_PI:
+    return XML_ROLE_PI;
+  case XML_TOK_COMMENT:
+    return XML_ROLE_COMMENT;
+  case XML_TOK_INSTANCE_START:
+    state->handler = error;
+    return XML_ROLE_INSTANCE_START;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype0(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = doctype1;
+    return XML_ROLE_DOCTYPE_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype1(PROLOG_STATE *state,
+         int tok,
+         const char *ptr,
+         const char *end,
+         const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_OPEN_BRACKET:
+    state->handler = internalSubset;
+    return XML_ROLE_DOCTYPE_INTERNAL_SUBSET;
+  case XML_TOK_DECL_CLOSE:
+    state->handler = prolog2;
+    return XML_ROLE_DOCTYPE_CLOSE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+      state->handler = doctype3;
+      return XML_ROLE_DOCTYPE_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+      state->handler = doctype2;
+      return XML_ROLE_DOCTYPE_NONE;
+    }
+    break;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype2(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = doctype3;
+    return XML_ROLE_DOCTYPE_PUBLIC_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype3(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = doctype4;
+    return XML_ROLE_DOCTYPE_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype4(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_OPEN_BRACKET:
+    state->handler = internalSubset;
+    return XML_ROLE_DOCTYPE_INTERNAL_SUBSET;
+  case XML_TOK_DECL_CLOSE:
+    state->handler = prolog2;
+    return XML_ROLE_DOCTYPE_CLOSE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+doctype5(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_DECL_CLOSE:
+    state->handler = prolog2;
+    return XML_ROLE_DOCTYPE_CLOSE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+internalSubset(PROLOG_STATE *state,
+               int tok,
+               const char *ptr,
+               const char *end,
+               const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_DECL_OPEN:
+    if (XmlNameMatchesAscii(enc,
+                            ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                            end,
+                            KW_ENTITY)) {
+      state->handler = entity0;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    if (XmlNameMatchesAscii(enc,
+                            ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                            end,
+                            KW_ATTLIST)) {
+      state->handler = attlist0;
+      return XML_ROLE_ATTLIST_NONE;
+    }
+    if (XmlNameMatchesAscii(enc,
+                            ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                            end,
+                            KW_ELEMENT)) {
+      state->handler = element0;
+      return XML_ROLE_ELEMENT_NONE;
+    }
+    if (XmlNameMatchesAscii(enc,
+                            ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                            end,
+                            KW_NOTATION)) {
+      state->handler = notation0;
+      return XML_ROLE_NOTATION_NONE;
+    }
+    break;
+  case XML_TOK_PI:
+    return XML_ROLE_PI;
+  case XML_TOK_COMMENT:
+    return XML_ROLE_COMMENT;
+  case XML_TOK_PARAM_ENTITY_REF:
+    return XML_ROLE_PARAM_ENTITY_REF;
+  case XML_TOK_CLOSE_BRACKET:
+    state->handler = doctype5;
+    return XML_ROLE_DOCTYPE_NONE;
+  case XML_TOK_NONE:
+    return XML_ROLE_NONE;
+  }
+  return common(state, tok);
+}
+
+#ifdef XML_DTD
+
+static int PTRCALL
+externalSubset0(PROLOG_STATE *state,
+                int tok,
+                const char *ptr,
+                const char *end,
+                const ENCODING *enc)
+{
+  state->handler = externalSubset1;
+  if (tok == XML_TOK_XML_DECL)
+    return XML_ROLE_TEXT_DECL;
+  return externalSubset1(state, tok, ptr, end, enc);
+}
+
+static int PTRCALL
+externalSubset1(PROLOG_STATE *state,
+                int tok,
+                const char *ptr,
+                const char *end,
+                const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_COND_SECT_OPEN:
+    state->handler = condSect0;
+    return XML_ROLE_NONE;
+  case XML_TOK_COND_SECT_CLOSE:
+    if (state->includeLevel == 0)
+      break;
+    state->includeLevel -= 1;
+    return XML_ROLE_NONE;
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_CLOSE_BRACKET:
+    break;
+  case XML_TOK_NONE:
+    if (state->includeLevel)
+      break;
+    return XML_ROLE_NONE;
+  default:
+    return internalSubset(state, tok, ptr, end, enc);
+  }
+  return common(state, tok);
+}
+
+#endif /* XML_DTD */
+
+static int PTRCALL
+entity0(PROLOG_STATE *state,
+        int tok,
+        const char *UNUSED_P(ptr),
+        const char *UNUSED_P(end),
+        const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_PERCENT:
+    state->handler = entity1;
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    state->handler = entity2;
+    return XML_ROLE_GENERAL_ENTITY_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity1(PROLOG_STATE *state,
+        int tok,
+        const char *UNUSED_P(ptr),
+        const char *UNUSED_P(end),
+        const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    state->handler = entity7;
+    return XML_ROLE_PARAM_ENTITY_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity2(PROLOG_STATE *state,
+        int tok,
+        const char *ptr,
+        const char *end,
+        const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+      state->handler = entity4;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+      state->handler = entity3;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    break;
+  case XML_TOK_LITERAL:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ENTITY_NONE;
+    return XML_ROLE_ENTITY_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity3(PROLOG_STATE *state,
+        int tok,
+        const char *UNUSED_P(ptr),
+        const char *UNUSED_P(end),
+        const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = entity4;
+    return XML_ROLE_ENTITY_PUBLIC_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity4(PROLOG_STATE *state,
+        int tok,
+        const char *UNUSED_P(ptr),
+        const char *UNUSED_P(end),
+        const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = entity5;
+    return XML_ROLE_ENTITY_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity5(PROLOG_STATE *state,
+        int tok,
+        const char *ptr,
+        const char *end,
+        const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return XML_ROLE_ENTITY_COMPLETE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) {
+      state->handler = entity6;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    break;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity6(PROLOG_STATE *state,
+        int tok,
+        const char *UNUSED_P(ptr),
+        const char *UNUSED_P(end),
+        const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ENTITY_NONE;
+    return XML_ROLE_ENTITY_NOTATION_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity7(PROLOG_STATE *state,
+        int tok,
+        const char *ptr,
+        const char *end,
+        const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+      state->handler = entity9;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+      state->handler = entity8;
+      return XML_ROLE_ENTITY_NONE;
+    }
+    break;
+  case XML_TOK_LITERAL:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ENTITY_NONE;
+    return XML_ROLE_ENTITY_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity8(PROLOG_STATE *state,
+        int tok,
+        const char *UNUSED_P(ptr),
+        const char *UNUSED_P(end),
+        const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = entity9;
+    return XML_ROLE_ENTITY_PUBLIC_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity9(PROLOG_STATE *state,
+        int tok,
+        const char *UNUSED_P(ptr),
+        const char *UNUSED_P(end),
+        const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = entity10;
+    return XML_ROLE_ENTITY_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+entity10(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ENTITY_NONE;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return XML_ROLE_ENTITY_COMPLETE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation0(PROLOG_STATE *state,
+          int tok,
+          const char *UNUSED_P(ptr),
+          const char *UNUSED_P(end),
+          const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_NAME:
+    state->handler = notation1;
+    return XML_ROLE_NOTATION_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation1(PROLOG_STATE *state,
+          int tok,
+          const char *ptr,
+          const char *end,
+          const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+      state->handler = notation3;
+      return XML_ROLE_NOTATION_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+      state->handler = notation2;
+      return XML_ROLE_NOTATION_NONE;
+    }
+    break;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation2(PROLOG_STATE *state,
+          int tok,
+          const char *UNUSED_P(ptr),
+          const char *UNUSED_P(end),
+          const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = notation4;
+    return XML_ROLE_NOTATION_PUBLIC_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation3(PROLOG_STATE *state,
+          int tok,
+          const char *UNUSED_P(ptr),
+          const char *UNUSED_P(end),
+          const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_NOTATION_NONE;
+    return XML_ROLE_NOTATION_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+notation4(PROLOG_STATE *state,
+          int tok,
+          const char *UNUSED_P(ptr),
+          const char *UNUSED_P(end),
+          const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NOTATION_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_NOTATION_NONE;
+    return XML_ROLE_NOTATION_SYSTEM_ID;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return XML_ROLE_NOTATION_NO_SYSTEM_ID;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist0(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = attlist1;
+    return XML_ROLE_ATTLIST_ELEMENT_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist1(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = attlist2;
+    return XML_ROLE_ATTRIBUTE_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist2(PROLOG_STATE *state,
+         int tok,
+         const char *ptr,
+         const char *end,
+         const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NAME:
+    {
+      static const char * const types[] = {
+        KW_CDATA,
+        KW_ID,
+        KW_IDREF,
+        KW_IDREFS,
+        KW_ENTITY,
+        KW_ENTITIES,
+        KW_NMTOKEN,
+        KW_NMTOKENS,
+      };
+      int i;
+      for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++)
+        if (XmlNameMatchesAscii(enc, ptr, end, types[i])) {
+          state->handler = attlist8;
+          return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i;
+        }
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) {
+      state->handler = attlist5;
+      return XML_ROLE_ATTLIST_NONE;
+    }
+    break;
+  case XML_TOK_OPEN_PAREN:
+    state->handler = attlist3;
+    return XML_ROLE_ATTLIST_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist3(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NMTOKEN:
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = attlist4;
+    return XML_ROLE_ATTRIBUTE_ENUM_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist4(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_CLOSE_PAREN:
+    state->handler = attlist8;
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_OR:
+    state->handler = attlist3;
+    return XML_ROLE_ATTLIST_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist5(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_OPEN_PAREN:
+    state->handler = attlist6;
+    return XML_ROLE_ATTLIST_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist6(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_NAME:
+    state->handler = attlist7;
+    return XML_ROLE_ATTRIBUTE_NOTATION_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist7(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_CLOSE_PAREN:
+    state->handler = attlist8;
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_OR:
+    state->handler = attlist6;
+    return XML_ROLE_ATTLIST_NONE;
+  }
+  return common(state, tok);
+}
+
+/* default value */
+static int PTRCALL
+attlist8(PROLOG_STATE *state,
+         int tok,
+         const char *ptr,
+         const char *end,
+         const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_POUND_NAME:
+    if (XmlNameMatchesAscii(enc,
+                            ptr + MIN_BYTES_PER_CHAR(enc),
+                            end,
+                            KW_IMPLIED)) {
+      state->handler = attlist1;
+      return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE;
+    }
+    if (XmlNameMatchesAscii(enc,
+                            ptr + MIN_BYTES_PER_CHAR(enc),
+                            end,
+                            KW_REQUIRED)) {
+      state->handler = attlist1;
+      return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE;
+    }
+    if (XmlNameMatchesAscii(enc,
+                            ptr + MIN_BYTES_PER_CHAR(enc),
+                            end,
+                            KW_FIXED)) {
+      state->handler = attlist9;
+      return XML_ROLE_ATTLIST_NONE;
+    }
+    break;
+  case XML_TOK_LITERAL:
+    state->handler = attlist1;
+    return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+attlist9(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ATTLIST_NONE;
+  case XML_TOK_LITERAL:
+    state->handler = attlist1;
+    return XML_ROLE_FIXED_ATTRIBUTE_VALUE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element0(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = element1;
+    return XML_ROLE_ELEMENT_NAME;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element1(PROLOG_STATE *state,
+         int tok,
+         const char *ptr,
+         const char *end,
+         const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+      return XML_ROLE_CONTENT_EMPTY;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+      return XML_ROLE_CONTENT_ANY;
+    }
+    break;
+  case XML_TOK_OPEN_PAREN:
+    state->handler = element2;
+    state->level = 1;
+    return XML_ROLE_GROUP_OPEN;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element2(PROLOG_STATE *state,
+         int tok,
+         const char *ptr,
+         const char *end,
+         const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_POUND_NAME:
+    if (XmlNameMatchesAscii(enc,
+                            ptr + MIN_BYTES_PER_CHAR(enc),
+                            end,
+                            KW_PCDATA)) {
+      state->handler = element3;
+      return XML_ROLE_CONTENT_PCDATA;
+    }
+    break;
+  case XML_TOK_OPEN_PAREN:
+    state->level = 2;
+    state->handler = element6;
+    return XML_ROLE_GROUP_OPEN;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT;
+  case XML_TOK_NAME_QUESTION:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_OPT;
+  case XML_TOK_NAME_ASTERISK:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_REP;
+  case XML_TOK_NAME_PLUS:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_PLUS;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element3(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_CLOSE_PAREN:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ELEMENT_NONE;
+    return XML_ROLE_GROUP_CLOSE;
+  case XML_TOK_CLOSE_PAREN_ASTERISK:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ELEMENT_NONE;
+    return XML_ROLE_GROUP_CLOSE_REP;
+  case XML_TOK_OR:
+    state->handler = element4;
+    return XML_ROLE_ELEMENT_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element4(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = element5;
+    return XML_ROLE_CONTENT_ELEMENT;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element5(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_CLOSE_PAREN_ASTERISK:
+    state->handler = declClose;
+    state->role_none = XML_ROLE_ELEMENT_NONE;
+    return XML_ROLE_GROUP_CLOSE_REP;
+  case XML_TOK_OR:
+    state->handler = element4;
+    return XML_ROLE_ELEMENT_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element6(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_OPEN_PAREN:
+    state->level += 1;
+    return XML_ROLE_GROUP_OPEN;
+  case XML_TOK_NAME:
+  case XML_TOK_PREFIXED_NAME:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT;
+  case XML_TOK_NAME_QUESTION:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_OPT;
+  case XML_TOK_NAME_ASTERISK:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_REP;
+  case XML_TOK_NAME_PLUS:
+    state->handler = element7;
+    return XML_ROLE_CONTENT_ELEMENT_PLUS;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+element7(PROLOG_STATE *state,
+         int tok,
+         const char *UNUSED_P(ptr),
+         const char *UNUSED_P(end),
+         const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_ELEMENT_NONE;
+  case XML_TOK_CLOSE_PAREN:
+    state->level -= 1;
+    if (state->level == 0) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+    }
+    return XML_ROLE_GROUP_CLOSE;
+  case XML_TOK_CLOSE_PAREN_ASTERISK:
+    state->level -= 1;
+    if (state->level == 0) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+    }
+    return XML_ROLE_GROUP_CLOSE_REP;
+  case XML_TOK_CLOSE_PAREN_QUESTION:
+    state->level -= 1;
+    if (state->level == 0) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+    }
+    return XML_ROLE_GROUP_CLOSE_OPT;
+  case XML_TOK_CLOSE_PAREN_PLUS:
+    state->level -= 1;
+    if (state->level == 0) {
+      state->handler = declClose;
+      state->role_none = XML_ROLE_ELEMENT_NONE;
+    }
+    return XML_ROLE_GROUP_CLOSE_PLUS;
+  case XML_TOK_COMMA:
+    state->handler = element6;
+    return XML_ROLE_GROUP_SEQUENCE;
+  case XML_TOK_OR:
+    state->handler = element6;
+    return XML_ROLE_GROUP_CHOICE;
+  }
+  return common(state, tok);
+}
+
+#ifdef XML_DTD
+
+static int PTRCALL
+condSect0(PROLOG_STATE *state,
+          int tok,
+          const char *ptr,
+          const char *end,
+          const ENCODING *enc)
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_NAME:
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) {
+      state->handler = condSect1;
+      return XML_ROLE_NONE;
+    }
+    if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) {
+      state->handler = condSect2;
+      return XML_ROLE_NONE;
+    }
+    break;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+condSect1(PROLOG_STATE *state,
+          int tok,
+          const char *UNUSED_P(ptr),
+          const char *UNUSED_P(end),
+          const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_OPEN_BRACKET:
+    state->handler = externalSubset1;
+    state->includeLevel += 1;
+    return XML_ROLE_NONE;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+condSect2(PROLOG_STATE *state,
+          int tok,
+          const char *UNUSED_P(ptr),
+          const char *UNUSED_P(end),
+          const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return XML_ROLE_NONE;
+  case XML_TOK_OPEN_BRACKET:
+    state->handler = externalSubset1;
+    return XML_ROLE_IGNORE_SECT;
+  }
+  return common(state, tok);
+}
+
+#endif /* XML_DTD */
+
+static int PTRCALL
+declClose(PROLOG_STATE *state,
+          int tok,
+          const char *UNUSED_P(ptr),
+          const char *UNUSED_P(end),
+          const ENCODING *UNUSED_P(enc))
+{
+  switch (tok) {
+  case XML_TOK_PROLOG_S:
+    return state->role_none;
+  case XML_TOK_DECL_CLOSE:
+    setTopLevel(state);
+    return state->role_none;
+  }
+  return common(state, tok);
+}
+
+static int PTRCALL
+error(PROLOG_STATE *UNUSED_P(state),
+      int UNUSED_P(tok),
+      const char *UNUSED_P(ptr),
+      const char *UNUSED_P(end),
+      const ENCODING *UNUSED_P(enc))
+{
+  return XML_ROLE_NONE;
+}
+
+static int FASTCALL
+common(PROLOG_STATE *state, int tok)
+{
+#ifdef XML_DTD
+  if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF)
+    return XML_ROLE_INNER_PARAM_ENTITY_REF;
+#endif
+  state->handler = error;
+  return XML_ROLE_ERROR;
+}
+
+void
+XmlPrologStateInit(PROLOG_STATE *state)
+{
+  state->handler = prolog0;
+#ifdef XML_DTD
+  state->documentEntity = 1;
+  state->includeLevel = 0;
+  state->inEntityValue = 0;
+#endif /* XML_DTD */
+}
+
+#ifdef XML_DTD
+
+void
+XmlPrologStateInitExternalEntity(PROLOG_STATE *state)
+{
+  state->handler = externalSubset0;
+  state->documentEntity = 0;
+  state->includeLevel = 0;
+}
+
+#endif /* XML_DTD */
diff --git a/deps/EXPAT/expat/xmlrole.h b/deps/EXPAT/expat/xmlrole.h
new file mode 100644
index 000000000..4dd9f06f9
--- /dev/null
+++ b/deps/EXPAT/expat/xmlrole.h
@@ -0,0 +1,114 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+#ifndef XmlRole_INCLUDED
+#define XmlRole_INCLUDED 1
+
+#ifdef __VMS
+/*      0        1         2         3      0        1         2         3
+        1234567890123456789012345678901     1234567890123456789012345678901 */
+#define XmlPrologStateInitExternalEntity    XmlPrologStateInitExternalEnt
+#endif
+
+#include "xmltok.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+  XML_ROLE_ERROR = -1,
+  XML_ROLE_NONE = 0,
+  XML_ROLE_XML_DECL,
+  XML_ROLE_INSTANCE_START,
+  XML_ROLE_DOCTYPE_NONE,
+  XML_ROLE_DOCTYPE_NAME,
+  XML_ROLE_DOCTYPE_SYSTEM_ID,
+  XML_ROLE_DOCTYPE_PUBLIC_ID,
+  XML_ROLE_DOCTYPE_INTERNAL_SUBSET,
+  XML_ROLE_DOCTYPE_CLOSE,
+  XML_ROLE_GENERAL_ENTITY_NAME,
+  XML_ROLE_PARAM_ENTITY_NAME,
+  XML_ROLE_ENTITY_NONE,
+  XML_ROLE_ENTITY_VALUE,
+  XML_ROLE_ENTITY_SYSTEM_ID,
+  XML_ROLE_ENTITY_PUBLIC_ID,
+  XML_ROLE_ENTITY_COMPLETE,
+  XML_ROLE_ENTITY_NOTATION_NAME,
+  XML_ROLE_NOTATION_NONE,
+  XML_ROLE_NOTATION_NAME,
+  XML_ROLE_NOTATION_SYSTEM_ID,
+  XML_ROLE_NOTATION_NO_SYSTEM_ID,
+  XML_ROLE_NOTATION_PUBLIC_ID,
+  XML_ROLE_ATTRIBUTE_NAME,
+  XML_ROLE_ATTRIBUTE_TYPE_CDATA,
+  XML_ROLE_ATTRIBUTE_TYPE_ID,
+  XML_ROLE_ATTRIBUTE_TYPE_IDREF,
+  XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
+  XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
+  XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
+  XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
+  XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
+  XML_ROLE_ATTRIBUTE_ENUM_VALUE,
+  XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
+  XML_ROLE_ATTLIST_NONE,
+  XML_ROLE_ATTLIST_ELEMENT_NAME,
+  XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
+  XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
+  XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
+  XML_ROLE_FIXED_ATTRIBUTE_VALUE,
+  XML_ROLE_ELEMENT_NONE,
+  XML_ROLE_ELEMENT_NAME,
+  XML_ROLE_CONTENT_ANY,
+  XML_ROLE_CONTENT_EMPTY,
+  XML_ROLE_CONTENT_PCDATA,
+  XML_ROLE_GROUP_OPEN,
+  XML_ROLE_GROUP_CLOSE,
+  XML_ROLE_GROUP_CLOSE_REP,
+  XML_ROLE_GROUP_CLOSE_OPT,
+  XML_ROLE_GROUP_CLOSE_PLUS,
+  XML_ROLE_GROUP_CHOICE,
+  XML_ROLE_GROUP_SEQUENCE,
+  XML_ROLE_CONTENT_ELEMENT,
+  XML_ROLE_CONTENT_ELEMENT_REP,
+  XML_ROLE_CONTENT_ELEMENT_OPT,
+  XML_ROLE_CONTENT_ELEMENT_PLUS,
+  XML_ROLE_PI,
+  XML_ROLE_COMMENT,
+#ifdef XML_DTD
+  XML_ROLE_TEXT_DECL,
+  XML_ROLE_IGNORE_SECT,
+  XML_ROLE_INNER_PARAM_ENTITY_REF,
+#endif /* XML_DTD */
+  XML_ROLE_PARAM_ENTITY_REF
+};
+
+typedef struct prolog_state {
+  int (PTRCALL *handler) (struct prolog_state *state,
+                          int tok,
+                          const char *ptr,
+                          const char *end,
+                          const ENCODING *enc);
+  unsigned level;
+  int role_none;
+#ifdef XML_DTD
+  unsigned includeLevel;
+  int documentEntity;
+  int inEntityValue;
+#endif /* XML_DTD */
+} PROLOG_STATE;
+
+void XmlPrologStateInit(PROLOG_STATE *);
+#ifdef XML_DTD
+void XmlPrologStateInitExternalEntity(PROLOG_STATE *);
+#endif /* XML_DTD */
+
+#define XmlTokenRole(state, tok, ptr, end, enc) \
+ (((state)->handler)(state, tok, ptr, end, enc))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlRole_INCLUDED */
diff --git a/deps/EXPAT/expat/xmltok.c b/deps/EXPAT/expat/xmltok.c
new file mode 100644
index 000000000..f10b459ff
--- /dev/null
+++ b/deps/EXPAT/expat/xmltok.c
@@ -0,0 +1,1737 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+#include <stddef.h>
+#include "expat_config.h"
+#include "expat_external.h"
+#include "internal.h"
+#include "xmltok.h"
+#include "nametab.h"
+
+#ifdef XML_DTD
+#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok)
+#else
+#define IGNORE_SECTION_TOK_VTABLE /* as nothing */
+#endif
+
+#define VTABLE1 \
+  { PREFIX(prologTok), PREFIX(contentTok), \
+    PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \
+  { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \
+  PREFIX(sameName), \
+  PREFIX(nameMatchesAscii), \
+  PREFIX(nameLength), \
+  PREFIX(skipS), \
+  PREFIX(getAtts), \
+  PREFIX(charRefNumber), \
+  PREFIX(predefinedEntityName), \
+  PREFIX(updatePosition), \
+  PREFIX(isPublicId)
+
+#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16)
+
+#define UCS2_GET_NAMING(pages, hi, lo) \
+   (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo) & 0x1F)))
+
+/* A 2 byte UTF-8 representation splits the characters 11 bits between
+   the bottom 5 and 6 bits of the bytes.  We need 8 bits to index into
+   pages, 3 bits to add to that index and 5 bits to generate the mask.
+*/
+#define UTF8_GET_NAMING2(pages, byte) \
+    (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \
+                      + ((((byte)[0]) & 3) << 1) \
+                      + ((((byte)[1]) >> 5) & 1)] \
+         & (1u << (((byte)[1]) & 0x1F)))
+
+/* A 3 byte UTF-8 representation splits the characters 16 bits between
+   the bottom 4, 6 and 6 bits of the bytes.  We need 8 bits to index
+   into pages, 3 bits to add to that index and 5 bits to generate the
+   mask.
+*/
+#define UTF8_GET_NAMING3(pages, byte) \
+  (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \
+                             + ((((byte)[1]) >> 2) & 0xF)] \
+                       << 3) \
+                      + ((((byte)[1]) & 3) << 1) \
+                      + ((((byte)[2]) >> 5) & 1)] \
+         & (1u << (((byte)[2]) & 0x1F)))
+
+#define UTF8_GET_NAMING(pages, p, n) \
+  ((n) == 2 \
+  ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \
+  : ((n) == 3 \
+     ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \
+     : 0))
+
+/* Detection of invalid UTF-8 sequences is based on Table 3.1B
+   of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
+   with the additional restriction of not allowing the Unicode
+   code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE).
+   Implementation details:
+     (A & 0x80) == 0     means A < 0x80
+   and
+     (A & 0xC0) == 0xC0  means A > 0xBF
+*/
+
+#define UTF8_INVALID2(p) \
+  ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0)
+
+#define UTF8_INVALID3(p) \
+  (((p)[2] & 0x80) == 0 \
+  || \
+  ((*p) == 0xEF && (p)[1] == 0xBF \
+    ? \
+    (p)[2] > 0xBD \
+    : \
+    ((p)[2] & 0xC0) == 0xC0) \
+  || \
+  ((*p) == 0xE0 \
+    ? \
+    (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \
+    : \
+    ((p)[1] & 0x80) == 0 \
+    || \
+    ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0)))
+
+#define UTF8_INVALID4(p) \
+  (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \
+  || \
+  ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \
+  || \
+  ((*p) == 0xF0 \
+    ? \
+    (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \
+    : \
+    ((p)[1] & 0x80) == 0 \
+    || \
+    ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0)))
+
+static int PTRFASTCALL
+isNever(const ENCODING *UNUSED_P(enc), const char *UNUSED_P(p))
+{
+  return 0;
+}
+
+static int PTRFASTCALL
+utf8_isName2(const ENCODING *UNUSED_P(enc), const char *p)
+{
+  return UTF8_GET_NAMING2(namePages, (const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isName3(const ENCODING *UNUSED_P(enc), const char *p)
+{
+  return UTF8_GET_NAMING3(namePages, (const unsigned char *)p);
+}
+
+#define utf8_isName4 isNever
+
+static int PTRFASTCALL
+utf8_isNmstrt2(const ENCODING *UNUSED_P(enc), const char *p)
+{
+  return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isNmstrt3(const ENCODING *UNUSED_P(enc), const char *p)
+{
+  return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p);
+}
+
+#define utf8_isNmstrt4 isNever
+
+static int PTRFASTCALL
+utf8_isInvalid2(const ENCODING *UNUSED_P(enc), const char *p)
+{
+  return UTF8_INVALID2((const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isInvalid3(const ENCODING *UNUSED_P(enc), const char *p)
+{
+  return UTF8_INVALID3((const unsigned char *)p);
+}
+
+static int PTRFASTCALL
+utf8_isInvalid4(const ENCODING *UNUSED_P(enc), const char *p)
+{
+  return UTF8_INVALID4((const unsigned char *)p);
+}
+
+struct normal_encoding {
+  ENCODING enc;
+  unsigned char type[256];
+#ifdef XML_MIN_SIZE
+  int (PTRFASTCALL *byteType)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *);
+  int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *);
+  int (PTRCALL *charMatches)(const ENCODING *, const char *, int);
+#endif /* XML_MIN_SIZE */
+  int (PTRFASTCALL *isName2)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isName3)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isName4)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *);
+  int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *);
+};
+
+#define AS_NORMAL_ENCODING(enc)   ((const struct normal_encoding *) (enc))
+
+#ifdef XML_MIN_SIZE
+
+#define STANDARD_VTABLE(E) \
+ E ## byteType, \
+ E ## isNameMin, \
+ E ## isNmstrtMin, \
+ E ## byteToAscii, \
+ E ## charMatches,
+
+#else
+
+#define STANDARD_VTABLE(E) /* as nothing */
+
+#endif
+
+#define NORMAL_VTABLE(E) \
+ E ## isName2, \
+ E ## isName3, \
+ E ## isName4, \
+ E ## isNmstrt2, \
+ E ## isNmstrt3, \
+ E ## isNmstrt4, \
+ E ## isInvalid2, \
+ E ## isInvalid3, \
+ E ## isInvalid4
+
+#define NULL_VTABLE \
+ /* isName2 */ NULL, \
+ /* isName3 */ NULL, \
+ /* isName4 */ NULL, \
+ /* isNmstrt2 */ NULL, \
+ /* isNmstrt3 */ NULL, \
+ /* isNmstrt4 */ NULL, \
+ /* isInvalid2 */ NULL, \
+ /* isInvalid3 */ NULL, \
+ /* isInvalid4 */ NULL
+
+static int FASTCALL checkCharRefNumber(int);
+
+#include "xmltok_impl.h"
+#include "ascii.h"
+
+#ifdef XML_MIN_SIZE
+#define sb_isNameMin isNever
+#define sb_isNmstrtMin isNever
+#endif
+
+#ifdef XML_MIN_SIZE
+#define MINBPC(enc) ((enc)->minBytesPerChar)
+#else
+/* minimum bytes per character */
+#define MINBPC(enc) 1
+#endif
+
+#define SB_BYTE_TYPE(enc, p) \
+  (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)])
+
+#ifdef XML_MIN_SIZE
+static int PTRFASTCALL
+sb_byteType(const ENCODING *enc, const char *p)
+{
+  return SB_BYTE_TYPE(enc, p);
+}
+#define BYTE_TYPE(enc, p) \
+ (AS_NORMAL_ENCODING(enc)->byteType(enc, p))
+#else
+#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define BYTE_TO_ASCII(enc, p) \
+ (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p))
+static int PTRFASTCALL
+sb_byteToAscii(const ENCODING *enc, const char *p)
+{
+  return *p;
+}
+#else
+#define BYTE_TO_ASCII(enc, p) (*(p))
+#endif
+
+#define IS_NAME_CHAR(enc, p, n) \
+ (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p))
+#define IS_NMSTRT_CHAR(enc, p, n) \
+ (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p))
+#define IS_INVALID_CHAR(enc, p, n) \
+ (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p))
+
+#ifdef XML_MIN_SIZE
+#define IS_NAME_CHAR_MINBPC(enc, p) \
+ (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p))
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p))
+#else
+#define IS_NAME_CHAR_MINBPC(enc, p) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define CHAR_MATCHES(enc, p, c) \
+ (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c))
+static int PTRCALL
+sb_charMatches(const ENCODING *enc, const char *p, int c)
+{
+  return *p == c;
+}
+#else
+/* c is an ASCII character */
+#define CHAR_MATCHES(enc, p, c) (*(p) == c)
+#endif
+
+#define PREFIX(ident) normal_ ## ident
+#define XML_TOK_IMPL_C
+#include "xmltok_impl.inc"
+#undef XML_TOK_IMPL_C
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+enum {  /* UTF8_cvalN is value of masked first byte of N byte sequence */
+  UTF8_cval1 = 0x00,
+  UTF8_cval2 = 0xc0,
+  UTF8_cval3 = 0xe0,
+  UTF8_cval4 = 0xf0
+};
+
+void
+align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef)
+{
+  const char * fromLim = *fromLimRef;
+  size_t walked = 0;
+  for (; fromLim > from; fromLim--, walked++) {
+    const unsigned char prev = (unsigned char)fromLim[-1];
+    if ((prev & 0xf8u) == 0xf0u) { /* 4-byte character, lead by 0b11110xxx byte */
+      if (walked + 1 >= 4) {
+        fromLim += 4 - 1;
+        break;
+      } else {
+        walked = 0;
+      }
+    } else if ((prev & 0xf0u) == 0xe0u) { /* 3-byte character, lead by 0b1110xxxx byte */
+      if (walked + 1 >= 3) {
+        fromLim += 3 - 1;
+        break;
+      } else {
+        walked = 0;
+      }
+    } else if ((prev & 0xe0u) == 0xc0u) { /* 2-byte character, lead by 0b110xxxxx byte */
+      if (walked + 1 >= 2) {
+        fromLim += 2 - 1;
+        break;
+      } else {
+        walked = 0;
+      }
+    } else if ((prev & 0x80u) == 0x00u) { /* 1-byte character, matching 0b0xxxxxxx */
+      break;
+    }
+  }
+  *fromLimRef = fromLim;
+}
+
+static enum XML_Convert_Result PTRCALL
+utf8_toUtf8(const ENCODING *UNUSED_P(enc),
+            const char **fromP, const char *fromLim,
+            char **toP, const char *toLim)
+{
+  enum XML_Convert_Result res = XML_CONVERT_COMPLETED;
+  char *to;
+  const char *from;
+  if (fromLim - *fromP > toLim - *toP) {
+    /* Avoid copying partial characters. */
+    res = XML_CONVERT_OUTPUT_EXHAUSTED;
+    fromLim = *fromP + (toLim - *toP);
+    align_limit_to_full_utf8_characters(*fromP, &fromLim);
+  }
+  for (to = *toP, from = *fromP; (from < fromLim) && (to < toLim); from++, to++)
+    *to = *from;
+  *fromP = from;
+  *toP = to;
+
+  if ((to == toLim) && (from < fromLim))
+    return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else
+    return res;
+}
+
+static enum XML_Convert_Result PTRCALL
+utf8_toUtf16(const ENCODING *enc,
+             const char **fromP, const char *fromLim,
+             unsigned short **toP, const unsigned short *toLim)
+{
+  enum XML_Convert_Result res = XML_CONVERT_COMPLETED;
+  unsigned short *to = *toP;
+  const char *from = *fromP;
+  while (from < fromLim && to < toLim) {
+    switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) {
+    case BT_LEAD2:
+      if (fromLim - from < 2) {
+        res = XML_CONVERT_INPUT_INCOMPLETE;
+        break;
+      }
+      *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f));
+      from += 2;
+      break;
+    case BT_LEAD3:
+      if (fromLim - from < 3) {
+        res = XML_CONVERT_INPUT_INCOMPLETE;
+        break;
+      }
+      *to++ = (unsigned short)(((from[0] & 0xf) << 12)
+                               | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f));
+      from += 3;
+      break;
+    case BT_LEAD4:
+      {
+        unsigned long n;
+        if (toLim - to < 2) {
+          res = XML_CONVERT_OUTPUT_EXHAUSTED;
+          goto after;
+        }
+        if (fromLim - from < 4) {
+          res = XML_CONVERT_INPUT_INCOMPLETE;
+          goto after;
+        }
+        n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12)
+            | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f);
+        n -= 0x10000;
+        to[0] = (unsigned short)((n >> 10) | 0xD800);
+        to[1] = (unsigned short)((n & 0x3FF) | 0xDC00);
+        to += 2;
+        from += 4;
+      }
+      break;
+    default:
+      *to++ = *from++;
+      break;
+    }
+  }
+after:
+  *fromP = from;
+  *toP = to;
+  return res;
+}
+
+#ifdef XML_NS
+static const struct normal_encoding utf8_encoding_ns = {
+  { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+  {
+#include "asciitab.h"
+#include "utf8tab.h"
+  },
+  STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+#endif
+
+static const struct normal_encoding utf8_encoding = {
+  { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+  {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+  },
+  STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_utf8_encoding_ns = {
+  { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+  {
+#include "iasciitab.h"
+#include "utf8tab.h"
+  },
+  STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+#endif
+
+static const struct normal_encoding internal_utf8_encoding = {
+  { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+  {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+  },
+  STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+static enum XML_Convert_Result PTRCALL
+latin1_toUtf8(const ENCODING *UNUSED_P(enc),
+              const char **fromP, const char *fromLim,
+              char **toP, const char *toLim)
+{
+  for (;;) {
+    unsigned char c;
+    if (*fromP == fromLim)
+      return XML_CONVERT_COMPLETED;
+    c = (unsigned char)**fromP;
+    if (c & 0x80) {
+      if (toLim - *toP < 2)
+        return XML_CONVERT_OUTPUT_EXHAUSTED;
+      *(*toP)++ = (char)((c >> 6) | UTF8_cval2);
+      *(*toP)++ = (char)((c & 0x3f) | 0x80);
+      (*fromP)++;
+    }
+    else {
+      if (*toP == toLim)
+        return XML_CONVERT_OUTPUT_EXHAUSTED;
+      *(*toP)++ = *(*fromP)++;
+    }
+  }
+}
+
+static enum XML_Convert_Result PTRCALL
+latin1_toUtf16(const ENCODING *UNUSED_P(enc),
+               const char **fromP, const char *fromLim,
+               unsigned short **toP, const unsigned short *toLim)
+{
+  while (*fromP < fromLim && *toP < toLim)
+    *(*toP)++ = (unsigned char)*(*fromP)++;
+
+  if ((*toP == toLim) && (*fromP < fromLim))
+    return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else
+    return XML_CONVERT_COMPLETED;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding latin1_encoding_ns = {
+  { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+  {
+#include "asciitab.h"
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(sb_) NULL_VTABLE
+};
+
+#endif
+
+static const struct normal_encoding latin1_encoding = {
+  { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+  {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(sb_) NULL_VTABLE
+};
+
+static enum XML_Convert_Result PTRCALL
+ascii_toUtf8(const ENCODING *UNUSED_P(enc),
+             const char **fromP, const char *fromLim,
+             char **toP, const char *toLim)
+{
+  while (*fromP < fromLim && *toP < toLim)
+    *(*toP)++ = *(*fromP)++;
+
+  if ((*toP == toLim) && (*fromP < fromLim))
+    return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else
+    return XML_CONVERT_COMPLETED;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding ascii_encoding_ns = {
+  { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+  {
+#include "asciitab.h"
+/* BT_NONXML == 0 */
+  },
+  STANDARD_VTABLE(sb_) NULL_VTABLE
+};
+
+#endif
+
+static const struct normal_encoding ascii_encoding = {
+  { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+  {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+/* BT_NONXML == 0 */
+  },
+  STANDARD_VTABLE(sb_) NULL_VTABLE
+};
+
+static int PTRFASTCALL
+unicode_byte_type(char hi, char lo)
+{
+  switch ((unsigned char)hi) {
+  case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+    return BT_LEAD4;
+  case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+    return BT_TRAIL;
+  case 0xFF:
+    switch ((unsigned char)lo) {
+    case 0xFF:
+    case 0xFE:
+      return BT_NONXML;
+    }
+    break;
+  }
+  return BT_NONASCII;
+}
+
+#define DEFINE_UTF16_TO_UTF8(E) \
+static enum XML_Convert_Result  PTRCALL \
+E ## toUtf8(const ENCODING *UNUSED_P(enc), \
+            const char **fromP, const char *fromLim, \
+            char **toP, const char *toLim) \
+{ \
+  const char *from = *fromP; \
+  fromLim = from + (((fromLim - from) >> 1) << 1);  /* shrink to even */ \
+  for (; from < fromLim; from += 2) { \
+    int plane; \
+    unsigned char lo2; \
+    unsigned char lo = GET_LO(from); \
+    unsigned char hi = GET_HI(from); \
+    switch (hi) { \
+    case 0: \
+      if (lo < 0x80) { \
+        if (*toP == toLim) { \
+          *fromP = from; \
+          return XML_CONVERT_OUTPUT_EXHAUSTED; \
+        } \
+        *(*toP)++ = lo; \
+        break; \
+      } \
+      /* fall through */ \
+    case 0x1: case 0x2: case 0x3: \
+    case 0x4: case 0x5: case 0x6: case 0x7: \
+      if (toLim -  *toP < 2) { \
+        *fromP = from; \
+        return XML_CONVERT_OUTPUT_EXHAUSTED; \
+      } \
+      *(*toP)++ = ((lo >> 6) | (hi << 2) |  UTF8_cval2); \
+      *(*toP)++ = ((lo & 0x3f) | 0x80); \
+      break; \
+    default: \
+      if (toLim -  *toP < 3)  { \
+        *fromP = from; \
+        return XML_CONVERT_OUTPUT_EXHAUSTED; \
+      } \
+      /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \
+      *(*toP)++ = ((hi >> 4) | UTF8_cval3); \
+      *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \
+      *(*toP)++ = ((lo & 0x3f) | 0x80); \
+      break; \
+    case 0xD8: case 0xD9: case 0xDA: case 0xDB: \
+      if (toLim -  *toP < 4) { \
+        *fromP = from; \
+        return XML_CONVERT_OUTPUT_EXHAUSTED; \
+      } \
+      if (fromLim - from < 4) { \
+        *fromP = from; \
+        return XML_CONVERT_INPUT_INCOMPLETE; \
+      } \
+      plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \
+      *(*toP)++ = ((plane >> 2) | UTF8_cval4); \
+      *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \
+      from += 2; \
+      lo2 = GET_LO(from); \
+      *(*toP)++ = (((lo & 0x3) << 4) \
+                   | ((GET_HI(from) & 0x3) << 2) \
+                   | (lo2 >> 6) \
+                   | 0x80); \
+      *(*toP)++ = ((lo2 & 0x3f) | 0x80); \
+      break; \
+    } \
+  } \
+  *fromP = from; \
+  if (from < fromLim) \
+    return XML_CONVERT_INPUT_INCOMPLETE; \
+  else \
+    return XML_CONVERT_COMPLETED; \
+}
+
+#define DEFINE_UTF16_TO_UTF16(E) \
+static enum XML_Convert_Result  PTRCALL \
+E ## toUtf16(const ENCODING *UNUSED_P(enc), \
+             const char **fromP, const char *fromLim, \
+             unsigned short **toP, const unsigned short *toLim) \
+{ \
+  enum XML_Convert_Result res = XML_CONVERT_COMPLETED; \
+  fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1);  /* shrink to even */ \
+  /* Avoid copying first half only of surrogate */ \
+  if (fromLim - *fromP > ((toLim - *toP) << 1) \
+      && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) { \
+    fromLim -= 2; \
+    res = XML_CONVERT_INPUT_INCOMPLETE; \
+  } \
+  for (; *fromP < fromLim && *toP < toLim; *fromP += 2) \
+    *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \
+  if ((*toP == toLim) && (*fromP < fromLim)) \
+    return XML_CONVERT_OUTPUT_EXHAUSTED; \
+  else \
+    return res; \
+}
+
+#define SET2(ptr, ch) \
+  (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[0])
+#define GET_HI(ptr) ((unsigned char)(ptr)[1])
+
+DEFINE_UTF16_TO_UTF8(little2_)
+DEFINE_UTF16_TO_UTF16(little2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define SET2(ptr, ch) \
+  (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[1])
+#define GET_HI(ptr) ((unsigned char)(ptr)[0])
+
+DEFINE_UTF16_TO_UTF8(big2_)
+DEFINE_UTF16_TO_UTF16(big2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define LITTLE2_BYTE_TYPE(enc, p) \
+ ((p)[1] == 0 \
+  ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \
+  : unicode_byte_type((p)[1], (p)[0]))
+#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1)
+#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c)
+#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \
+  UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0])
+#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+  UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0])
+
+#ifdef XML_MIN_SIZE
+
+static int PTRFASTCALL
+little2_byteType(const ENCODING *enc, const char *p)
+{
+  return LITTLE2_BYTE_TYPE(enc, p);
+}
+
+static int PTRFASTCALL
+little2_byteToAscii(const ENCODING *enc, const char *p)
+{
+  return LITTLE2_BYTE_TO_ASCII(enc, p);
+}
+
+static int PTRCALL
+little2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+  return LITTLE2_CHAR_MATCHES(enc, p, c);
+}
+
+static int PTRFASTCALL
+little2_isNameMin(const ENCODING *enc, const char *p)
+{
+  return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static int PTRFASTCALL
+little2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+  return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) little2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p)
+#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#define XML_TOK_IMPL_C
+#include "xmltok_impl.inc"
+#undef XML_TOK_IMPL_C
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding little2_encoding_ns = {
+  { VTABLE, 2, 0,
+#if BYTEORDER == 1234
+    1
+#else
+    0
+#endif
+  },
+  {
+#include "asciitab.h"
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(little2_) NULL_VTABLE
+};
+
+#endif
+
+static const struct normal_encoding little2_encoding = {
+  { VTABLE, 2, 0,
+#if BYTEORDER == 1234
+    1
+#else
+    0
+#endif
+  },
+  {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(little2_) NULL_VTABLE
+};
+
+#if BYTEORDER != 4321
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_little2_encoding_ns = {
+  { VTABLE, 2, 0, 1 },
+  {
+#include "iasciitab.h"
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(little2_) NULL_VTABLE
+};
+
+#endif
+
+static const struct normal_encoding internal_little2_encoding = {
+  { VTABLE, 2, 0, 1 },
+  {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(little2_) NULL_VTABLE
+};
+
+#endif
+
+
+#define BIG2_BYTE_TYPE(enc, p) \
+ ((p)[0] == 0 \
+  ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \
+  : unicode_byte_type((p)[0], (p)[1]))
+#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1)
+#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c)
+#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \
+  UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1])
+#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+  UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1])
+
+#ifdef XML_MIN_SIZE
+
+static int PTRFASTCALL
+big2_byteType(const ENCODING *enc, const char *p)
+{
+  return BIG2_BYTE_TYPE(enc, p);
+}
+
+static int PTRFASTCALL
+big2_byteToAscii(const ENCODING *enc, const char *p)
+{
+  return BIG2_BYTE_TO_ASCII(enc, p);
+}
+
+static int PTRCALL
+big2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+  return BIG2_CHAR_MATCHES(enc, p, c);
+}
+
+static int PTRFASTCALL
+big2_isNameMin(const ENCODING *enc, const char *p)
+{
+  return BIG2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static int PTRFASTCALL
+big2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+  return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) big2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p)
+#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#define XML_TOK_IMPL_C
+#include "xmltok_impl.inc"
+#undef XML_TOK_IMPL_C
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding big2_encoding_ns = {
+  { VTABLE, 2, 0,
+#if BYTEORDER == 4321
+  1
+#else
+  0
+#endif
+  },
+  {
+#include "asciitab.h"
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(big2_) NULL_VTABLE
+};
+
+#endif
+
+static const struct normal_encoding big2_encoding = {
+  { VTABLE, 2, 0,
+#if BYTEORDER == 4321
+  1
+#else
+  0
+#endif
+  },
+  {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(big2_) NULL_VTABLE
+};
+
+#if BYTEORDER != 1234
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_big2_encoding_ns = {
+  { VTABLE, 2, 0, 1 },
+  {
+#include "iasciitab.h"
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(big2_) NULL_VTABLE
+};
+
+#endif
+
+static const struct normal_encoding internal_big2_encoding = {
+  { VTABLE, 2, 0, 1 },
+  {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+  },
+  STANDARD_VTABLE(big2_) NULL_VTABLE
+};
+
+#endif
+
+#undef PREFIX
+
+static int FASTCALL
+streqci(const char *s1, const char *s2)
+{
+  for (;;) {
+    char c1 = *s1++;
+    char c2 = *s2++;
+    if (ASCII_a <= c1 && c1 <= ASCII_z)
+      c1 += ASCII_A - ASCII_a;
+    if (ASCII_a <= c2 && c2 <= ASCII_z)
+      c2 += ASCII_A - ASCII_a;
+    if (c1 != c2)
+      return 0;
+    if (!c1)
+      break;
+  }
+  return 1;
+}
+
+static void PTRCALL
+initUpdatePosition(const ENCODING *UNUSED_P(enc), const char *ptr,
+                   const char *end, POSITION *pos)
+{
+  normal_updatePosition(&utf8_encoding.enc, ptr, end, pos);
+}
+
+static int
+toAscii(const ENCODING *enc, const char *ptr, const char *end)
+{
+  char buf[1];
+  char *p = buf;
+  XmlUtf8Convert(enc, &ptr, end, &p, p + 1);
+  if (p == buf)
+    return -1;
+  else
+    return buf[0];
+}
+
+static int FASTCALL
+isSpace(int c)
+{
+  switch (c) {
+  case 0x20:
+  case 0xD:
+  case 0xA:
+  case 0x9:
+    return 1;
+  }
+  return 0;
+}
+
+/* Return 1 if there's just optional white space or there's an S
+   followed by name=val.
+*/
+static int
+parsePseudoAttribute(const ENCODING *enc,
+                     const char *ptr,
+                     const char *end,
+                     const char **namePtr,
+                     const char **nameEndPtr,
+                     const char **valPtr,
+                     const char **nextTokPtr)
+{
+  int c;
+  char open;
+  if (ptr == end) {
+    *namePtr = NULL;
+    return 1;
+  }
+  if (!isSpace(toAscii(enc, ptr, end))) {
+    *nextTokPtr = ptr;
+    return 0;
+  }
+  do {
+    ptr += enc->minBytesPerChar;
+  } while (isSpace(toAscii(enc, ptr, end)));
+  if (ptr == end) {
+    *namePtr = NULL;
+    return 1;
+  }
+  *namePtr = ptr;
+  for (;;) {
+    c = toAscii(enc, ptr, end);
+    if (c == -1) {
+      *nextTokPtr = ptr;
+      return 0;
+    }
+    if (c == ASCII_EQUALS) {
+      *nameEndPtr = ptr;
+      break;
+    }
+    if (isSpace(c)) {
+      *nameEndPtr = ptr;
+      do {
+        ptr += enc->minBytesPerChar;
+      } while (isSpace(c = toAscii(enc, ptr, end)));
+      if (c != ASCII_EQUALS) {
+        *nextTokPtr = ptr;
+        return 0;
+      }
+      break;
+    }
+    ptr += enc->minBytesPerChar;
+  }
+  if (ptr == *namePtr) {
+    *nextTokPtr = ptr;
+    return 0;
+  }
+  ptr += enc->minBytesPerChar;
+  c = toAscii(enc, ptr, end);
+  while (isSpace(c)) {
+    ptr += enc->minBytesPerChar;
+    c = toAscii(enc, ptr, end);
+  }
+  if (c != ASCII_QUOT && c != ASCII_APOS) {
+    *nextTokPtr = ptr;
+    return 0;
+  }
+  open = (char)c;
+  ptr += enc->minBytesPerChar;
+  *valPtr = ptr;
+  for (;; ptr += enc->minBytesPerChar) {
+    c = toAscii(enc, ptr, end);
+    if (c == open)
+      break;
+    if (!(ASCII_a <= c && c <= ASCII_z)
+        && !(ASCII_A <= c && c <= ASCII_Z)
+        && !(ASCII_0 <= c && c <= ASCII_9)
+        && c != ASCII_PERIOD
+        && c != ASCII_MINUS
+        && c != ASCII_UNDERSCORE) {
+      *nextTokPtr = ptr;
+      return 0;
+    }
+  }
+  *nextTokPtr = ptr + enc->minBytesPerChar;
+  return 1;
+}
+
+static const char KW_version[] = {
+  ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0'
+};
+
+static const char KW_encoding[] = {
+  ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0'
+};
+
+static const char KW_standalone[] = {
+  ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o,
+  ASCII_n, ASCII_e, '\0'
+};
+
+static const char KW_yes[] = {
+  ASCII_y, ASCII_e, ASCII_s,  '\0'
+};
+
+static const char KW_no[] = {
+  ASCII_n, ASCII_o,  '\0'
+};
+
+static int
+doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *,
+                                                 const char *,
+                                                 const char *),
+               int isGeneralTextEntity,
+               const ENCODING *enc,
+               const char *ptr,
+               const char *end,
+               const char **badPtr,
+               const char **versionPtr,
+               const char **versionEndPtr,
+               const char **encodingName,
+               const ENCODING **encoding,
+               int *standalone)
+{
+  const char *val = NULL;
+  const char *name = NULL;
+  const char *nameEnd = NULL;
+  ptr += 5 * enc->minBytesPerChar;
+  end -= 2 * enc->minBytesPerChar;
+  if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)
+      || !name) {
+    *badPtr = ptr;
+    return 0;
+  }
+  if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) {
+    if (!isGeneralTextEntity) {
+      *badPtr = name;
+      return 0;
+    }
+  }
+  else {
+    if (versionPtr)
+      *versionPtr = val;
+    if (versionEndPtr)
+      *versionEndPtr = ptr;
+    if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) {
+      *badPtr = ptr;
+      return 0;
+    }
+    if (!name) {
+      if (isGeneralTextEntity) {
+        /* a TextDecl must have an EncodingDecl */
+        *badPtr = ptr;
+        return 0;
+      }
+      return 1;
+    }
+  }
+  if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) {
+    int c = toAscii(enc, val, end);
+    if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) {
+      *badPtr = val;
+      return 0;
+    }
+    if (encodingName)
+      *encodingName = val;
+    if (encoding)
+      *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar);
+    if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) {
+      *badPtr = ptr;
+      return 0;
+    }
+    if (!name)
+      return 1;
+  }
+  if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone)
+      || isGeneralTextEntity) {
+    *badPtr = name;
+    return 0;
+  }
+  if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) {
+    if (standalone)
+      *standalone = 1;
+  }
+  else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) {
+    if (standalone)
+      *standalone = 0;
+  }
+  else {
+    *badPtr = val;
+    return 0;
+  }
+  while (isSpace(toAscii(enc, ptr, end)))
+    ptr += enc->minBytesPerChar;
+  if (ptr != end) {
+    *badPtr = ptr;
+    return 0;
+  }
+  return 1;
+}
+
+static int FASTCALL
+checkCharRefNumber(int result)
+{
+  switch (result >> 8) {
+  case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+  case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+    return -1;
+  case 0:
+    if (latin1_encoding.type[result] == BT_NONXML)
+      return -1;
+    break;
+  case 0xFF:
+    if (result == 0xFFFE || result == 0xFFFF)
+      return -1;
+    break;
+  }
+  return result;
+}
+
+int FASTCALL
+XmlUtf8Encode(int c, char *buf)
+{
+  enum {
+    /* minN is minimum legal resulting value for N byte sequence */
+    min2 = 0x80,
+    min3 = 0x800,
+    min4 = 0x10000
+  };
+
+  if (c < 0)
+    return 0;
+  if (c < min2) {
+    buf[0] = (char)(c | UTF8_cval1);
+    return 1;
+  }
+  if (c < min3) {
+    buf[0] = (char)((c >> 6) | UTF8_cval2);
+    buf[1] = (char)((c & 0x3f) | 0x80);
+    return 2;
+  }
+  if (c < min4) {
+    buf[0] = (char)((c >> 12) | UTF8_cval3);
+    buf[1] = (char)(((c >> 6) & 0x3f) | 0x80);
+    buf[2] = (char)((c & 0x3f) | 0x80);
+    return 3;
+  }
+  if (c < 0x110000) {
+    buf[0] = (char)((c >> 18) | UTF8_cval4);
+    buf[1] = (char)(((c >> 12) & 0x3f) | 0x80);
+    buf[2] = (char)(((c >> 6) & 0x3f) | 0x80);
+    buf[3] = (char)((c & 0x3f) | 0x80);
+    return 4;
+  }
+  return 0;
+}
+
+int FASTCALL
+XmlUtf16Encode(int charNum, unsigned short *buf)
+{
+  if (charNum < 0)
+    return 0;
+  if (charNum < 0x10000) {
+    buf[0] = (unsigned short)charNum;
+    return 1;
+  }
+  if (charNum < 0x110000) {
+    charNum -= 0x10000;
+    buf[0] = (unsigned short)((charNum >> 10) + 0xD800);
+    buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00);
+    return 2;
+  }
+  return 0;
+}
+
+struct unknown_encoding {
+  struct normal_encoding normal;
+  CONVERTER convert;
+  void *userData;
+  unsigned short utf16[256];
+  char utf8[256][4];
+};
+
+#define AS_UNKNOWN_ENCODING(enc)  ((const struct unknown_encoding *) (enc))
+
+int
+XmlSizeOfUnknownEncoding(void)
+{
+  return sizeof(struct unknown_encoding);
+}
+
+static int PTRFASTCALL
+unknown_isName(const ENCODING *enc, const char *p)
+{
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  int c = uenc->convert(uenc->userData, p);
+  if (c & ~0xFFFF)
+    return 0;
+  return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF);
+}
+
+static int PTRFASTCALL
+unknown_isNmstrt(const ENCODING *enc, const char *p)
+{
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  int c = uenc->convert(uenc->userData, p);
+  if (c & ~0xFFFF)
+    return 0;
+  return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF);
+}
+
+static int PTRFASTCALL
+unknown_isInvalid(const ENCODING *enc, const char *p)
+{
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  int c = uenc->convert(uenc->userData, p);
+  return (c & ~0xFFFF) || checkCharRefNumber(c) < 0;
+}
+
+static enum XML_Convert_Result PTRCALL
+unknown_toUtf8(const ENCODING *enc,
+               const char **fromP, const char *fromLim,
+               char **toP, const char *toLim)
+{
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  char buf[XML_UTF8_ENCODE_MAX];
+  for (;;) {
+    const char *utf8;
+    int n;
+    if (*fromP == fromLim)
+      return XML_CONVERT_COMPLETED;
+    utf8 = uenc->utf8[(unsigned char)**fromP];
+    n = *utf8++;
+    if (n == 0) {
+      int c = uenc->convert(uenc->userData, *fromP);
+      n = XmlUtf8Encode(c, buf);
+      if (n > toLim - *toP)
+        return XML_CONVERT_OUTPUT_EXHAUSTED;
+      utf8 = buf;
+      *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP]
+                 - (BT_LEAD2 - 2));
+    }
+    else {
+      if (n > toLim - *toP)
+        return XML_CONVERT_OUTPUT_EXHAUSTED;
+      (*fromP)++;
+    }
+    do {
+      *(*toP)++ = *utf8++;
+    } while (--n != 0);
+  }
+}
+
+static enum XML_Convert_Result PTRCALL
+unknown_toUtf16(const ENCODING *enc,
+                const char **fromP, const char *fromLim,
+                unsigned short **toP, const unsigned short *toLim)
+{
+  const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc);
+  while (*fromP < fromLim && *toP < toLim) {
+    unsigned short c = uenc->utf16[(unsigned char)**fromP];
+    if (c == 0) {
+      c = (unsigned short)
+          uenc->convert(uenc->userData, *fromP);
+      *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP]
+                 - (BT_LEAD2 - 2));
+    }
+    else
+      (*fromP)++;
+    *(*toP)++ = c;
+  }
+
+  if ((*toP == toLim) && (*fromP < fromLim))
+    return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else
+    return XML_CONVERT_COMPLETED;
+}
+
+ENCODING *
+XmlInitUnknownEncoding(void *mem,
+                       int *table,
+                       CONVERTER convert,
+                       void *userData)
+{
+  int i;
+  struct unknown_encoding *e = (struct unknown_encoding *)mem;
+  for (i = 0; i < (int)sizeof(struct normal_encoding); i++)
+    ((char *)mem)[i] = ((char *)&latin1_encoding)[i];
+  for (i = 0; i < 128; i++)
+    if (latin1_encoding.type[i] != BT_OTHER
+        && latin1_encoding.type[i] != BT_NONXML
+        && table[i] != i)
+      return 0;
+  for (i = 0; i < 256; i++) {
+    int c = table[i];
+    if (c == -1) {
+      e->normal.type[i] = BT_MALFORM;
+      /* This shouldn't really get used. */
+      e->utf16[i] = 0xFFFF;
+      e->utf8[i][0] = 1;
+      e->utf8[i][1] = 0;
+    }
+    else if (c < 0) {
+      if (c < -4)
+        return 0;
+      e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2));
+      e->utf8[i][0] = 0;
+      e->utf16[i] = 0;
+    }
+    else if (c < 0x80) {
+      if (latin1_encoding.type[c] != BT_OTHER
+          && latin1_encoding.type[c] != BT_NONXML
+          && c != i)
+        return 0;
+      e->normal.type[i] = latin1_encoding.type[c];
+      e->utf8[i][0] = 1;
+      e->utf8[i][1] = (char)c;
+      e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c);
+    }
+    else if (checkCharRefNumber(c) < 0) {
+      e->normal.type[i] = BT_NONXML;
+      /* This shouldn't really get used. */
+      e->utf16[i] = 0xFFFF;
+      e->utf8[i][0] = 1;
+      e->utf8[i][1] = 0;
+    }
+    else {
+      if (c > 0xFFFF)
+        return 0;
+      if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff))
+        e->normal.type[i] = BT_NMSTRT;
+      else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff))
+        e->normal.type[i] = BT_NAME;
+      else
+        e->normal.type[i] = BT_OTHER;
+      e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1);
+      e->utf16[i] = (unsigned short)c;
+    }
+  }
+  e->userData = userData;
+  e->convert = convert;
+  if (convert) {
+    e->normal.isName2 = unknown_isName;
+    e->normal.isName3 = unknown_isName;
+    e->normal.isName4 = unknown_isName;
+    e->normal.isNmstrt2 = unknown_isNmstrt;
+    e->normal.isNmstrt3 = unknown_isNmstrt;
+    e->normal.isNmstrt4 = unknown_isNmstrt;
+    e->normal.isInvalid2 = unknown_isInvalid;
+    e->normal.isInvalid3 = unknown_isInvalid;
+    e->normal.isInvalid4 = unknown_isInvalid;
+  }
+  e->normal.enc.utf8Convert = unknown_toUtf8;
+  e->normal.enc.utf16Convert = unknown_toUtf16;
+  return &(e->normal.enc);
+}
+
+/* If this enumeration is changed, getEncodingIndex and encodings
+must also be changed. */
+enum {
+  UNKNOWN_ENC = -1,
+  ISO_8859_1_ENC = 0,
+  US_ASCII_ENC,
+  UTF_8_ENC,
+  UTF_16_ENC,
+  UTF_16BE_ENC,
+  UTF_16LE_ENC,
+  /* must match encodingNames up to here */
+  NO_ENC
+};
+
+static const char KW_ISO_8859_1[] = {
+  ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9,
+  ASCII_MINUS, ASCII_1, '\0'
+};
+static const char KW_US_ASCII[] = {
+  ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I,
+  '\0'
+};
+static const char KW_UTF_8[] =  {
+  ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0'
+};
+static const char KW_UTF_16[] = {
+  ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0'
+};
+static const char KW_UTF_16BE[] = {
+  ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E,
+  '\0'
+};
+static const char KW_UTF_16LE[] = {
+  ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E,
+  '\0'
+};
+
+static int FASTCALL
+getEncodingIndex(const char *name)
+{
+  static const char * const encodingNames[] = {
+    KW_ISO_8859_1,
+    KW_US_ASCII,
+    KW_UTF_8,
+    KW_UTF_16,
+    KW_UTF_16BE,
+    KW_UTF_16LE,
+  };
+  int i;
+  if (name == NULL)
+    return NO_ENC;
+  for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++)
+    if (streqci(name, encodingNames[i]))
+      return i;
+  return UNKNOWN_ENC;
+}
+
+/* For binary compatibility, we store the index of the encoding
+   specified at initialization in the isUtf16 member.
+*/
+
+#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16)
+#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i)
+
+/* This is what detects the encoding.  encodingTable maps from
+   encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of
+   the external (protocol) specified encoding; state is
+   XML_CONTENT_STATE if we're parsing an external text entity, and
+   XML_PROLOG_STATE otherwise.
+*/
+
+
+static int
+initScan(const ENCODING * const *encodingTable,
+         const INIT_ENCODING *enc,
+         int state,
+         const char *ptr,
+         const char *end,
+         const char **nextTokPtr)
+{
+  const ENCODING **encPtr;
+
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  encPtr = enc->encPtr;
+  if (ptr + 1 == end) {
+    /* only a single byte available for auto-detection */
+#ifndef XML_DTD /* FIXME */
+    /* a well-formed document entity must have more than one byte */
+    if (state != XML_CONTENT_STATE)
+      return XML_TOK_PARTIAL;
+#endif
+    /* so we're parsing an external text entity... */
+    /* if UTF-16 was externally specified, then we need at least 2 bytes */
+    switch (INIT_ENC_INDEX(enc)) {
+    case UTF_16_ENC:
+    case UTF_16LE_ENC:
+    case UTF_16BE_ENC:
+      return XML_TOK_PARTIAL;
+    }
+    switch ((unsigned char)*ptr) {
+    case 0xFE:
+    case 0xFF:
+    case 0xEF: /* possibly first byte of UTF-8 BOM */
+      if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+          && state == XML_CONTENT_STATE)
+        break;
+      /* fall through */
+    case 0x00:
+    case 0x3C:
+      return XML_TOK_PARTIAL;
+    }
+  }
+  else {
+    switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) {
+    case 0xFEFF:
+      if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+          && state == XML_CONTENT_STATE)
+        break;
+      *nextTokPtr = ptr + 2;
+      *encPtr = encodingTable[UTF_16BE_ENC];
+      return XML_TOK_BOM;
+    /* 00 3C is handled in the default case */
+    case 0x3C00:
+      if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC
+           || INIT_ENC_INDEX(enc) == UTF_16_ENC)
+          && state == XML_CONTENT_STATE)
+        break;
+      *encPtr = encodingTable[UTF_16LE_ENC];
+      return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+    case 0xFFFE:
+      if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+          && state == XML_CONTENT_STATE)
+        break;
+      *nextTokPtr = ptr + 2;
+      *encPtr = encodingTable[UTF_16LE_ENC];
+      return XML_TOK_BOM;
+    case 0xEFBB:
+      /* Maybe a UTF-8 BOM (EF BB BF) */
+      /* If there's an explicitly specified (external) encoding
+         of ISO-8859-1 or some flavour of UTF-16
+         and this is an external text entity,
+         don't look for the BOM,
+         because it might be a legal data.
+      */
+      if (state == XML_CONTENT_STATE) {
+        int e = INIT_ENC_INDEX(enc);
+        if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC
+            || e == UTF_16LE_ENC || e == UTF_16_ENC)
+          break;
+      }
+      if (ptr + 2 == end)
+        return XML_TOK_PARTIAL;
+      if ((unsigned char)ptr[2] == 0xBF) {
+        *nextTokPtr = ptr + 3;
+        *encPtr = encodingTable[UTF_8_ENC];
+        return XML_TOK_BOM;
+      }
+      break;
+    default:
+      if (ptr[0] == '\0') {
+        /* 0 isn't a legal data character. Furthermore a document
+           entity can only start with ASCII characters.  So the only
+           way this can fail to be big-endian UTF-16 if it it's an
+           external parsed general entity that's labelled as
+           UTF-16LE.
+        */
+        if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC)
+          break;
+        *encPtr = encodingTable[UTF_16BE_ENC];
+        return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+      }
+      else if (ptr[1] == '\0') {
+        /* We could recover here in the case:
+            - parsing an external entity
+            - second byte is 0
+            - no externally specified encoding
+            - no encoding declaration
+           by assuming UTF-16LE.  But we don't, because this would mean when
+           presented just with a single byte, we couldn't reliably determine
+           whether we needed further bytes.
+        */
+        if (state == XML_CONTENT_STATE)
+          break;
+        *encPtr = encodingTable[UTF_16LE_ENC];
+        return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+      }
+      break;
+    }
+  }
+  *encPtr = encodingTable[INIT_ENC_INDEX(enc)];
+  return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+}
+
+
+#define NS(x) x
+#define ns(x) x
+#define XML_TOK_NS_C
+#include "xmltok_ns.inc"
+#undef XML_TOK_NS_C
+#undef NS
+#undef ns
+
+#ifdef XML_NS
+
+#define NS(x) x ## NS
+#define ns(x) x ## _ns
+
+#define XML_TOK_NS_C
+#include "xmltok_ns.inc"
+#undef XML_TOK_NS_C
+
+#undef NS
+#undef ns
+
+ENCODING *
+XmlInitUnknownEncodingNS(void *mem,
+                         int *table,
+                         CONVERTER convert,
+                         void *userData)
+{
+  ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData);
+  if (enc)
+    ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON;
+  return enc;
+}
+
+#endif /* XML_NS */
diff --git a/deps/EXPAT/expat/xmltok.h b/deps/EXPAT/expat/xmltok.h
new file mode 100644
index 000000000..752007e8b
--- /dev/null
+++ b/deps/EXPAT/expat/xmltok.h
@@ -0,0 +1,322 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+#ifndef XmlTok_INCLUDED
+#define XmlTok_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following token may be returned by XmlContentTok */
+#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be
+                                    start of illegal ]]> sequence */
+/* The following tokens may be returned by both XmlPrologTok and
+   XmlContentTok.
+*/
+#define XML_TOK_NONE -4          /* The string to be scanned is empty */
+#define XML_TOK_TRAILING_CR -3   /* A CR at the end of the scan;
+                                    might be part of CRLF sequence */
+#define XML_TOK_PARTIAL_CHAR -2  /* only part of a multibyte sequence */
+#define XML_TOK_PARTIAL -1       /* only part of a token */
+#define XML_TOK_INVALID 0
+
+/* The following tokens are returned by XmlContentTok; some are also
+   returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok.
+*/
+#define XML_TOK_START_TAG_WITH_ATTS 1
+#define XML_TOK_START_TAG_NO_ATTS 2
+#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
+#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
+#define XML_TOK_END_TAG 5
+#define XML_TOK_DATA_CHARS 6
+#define XML_TOK_DATA_NEWLINE 7
+#define XML_TOK_CDATA_SECT_OPEN 8
+#define XML_TOK_ENTITY_REF 9
+#define XML_TOK_CHAR_REF 10               /* numeric character reference */
+
+/* The following tokens may be returned by both XmlPrologTok and
+   XmlContentTok.
+*/
+#define XML_TOK_PI 11                     /* processing instruction */
+#define XML_TOK_XML_DECL 12               /* XML decl or text decl */
+#define XML_TOK_COMMENT 13
+#define XML_TOK_BOM 14                    /* Byte order mark */
+
+/* The following tokens are returned only by XmlPrologTok */
+#define XML_TOK_PROLOG_S 15
+#define XML_TOK_DECL_OPEN 16              /* <!foo */
+#define XML_TOK_DECL_CLOSE 17             /* > */
+#define XML_TOK_NAME 18
+#define XML_TOK_NMTOKEN 19
+#define XML_TOK_POUND_NAME 20             /* #name */
+#define XML_TOK_OR 21                     /* | */
+#define XML_TOK_PERCENT 22
+#define XML_TOK_OPEN_PAREN 23
+#define XML_TOK_CLOSE_PAREN 24
+#define XML_TOK_OPEN_BRACKET 25
+#define XML_TOK_CLOSE_BRACKET 26
+#define XML_TOK_LITERAL 27
+#define XML_TOK_PARAM_ENTITY_REF 28
+#define XML_TOK_INSTANCE_START 29
+
+/* The following occur only in element type declarations */
+#define XML_TOK_NAME_QUESTION 30          /* name? */
+#define XML_TOK_NAME_ASTERISK 31          /* name* */
+#define XML_TOK_NAME_PLUS 32              /* name+ */
+#define XML_TOK_COND_SECT_OPEN 33         /* <![ */
+#define XML_TOK_COND_SECT_CLOSE 34        /* ]]> */
+#define XML_TOK_CLOSE_PAREN_QUESTION 35   /* )? */
+#define XML_TOK_CLOSE_PAREN_ASTERISK 36   /* )* */
+#define XML_TOK_CLOSE_PAREN_PLUS 37       /* )+ */
+#define XML_TOK_COMMA 38
+
+/* The following token is returned only by XmlAttributeValueTok */
+#define XML_TOK_ATTRIBUTE_VALUE_S 39
+
+/* The following token is returned only by XmlCdataSectionTok */
+#define XML_TOK_CDATA_SECT_CLOSE 40
+
+/* With namespace processing this is returned by XmlPrologTok for a
+   name with a colon.
+*/
+#define XML_TOK_PREFIXED_NAME 41
+
+#ifdef XML_DTD
+#define XML_TOK_IGNORE_SECT 42
+#endif /* XML_DTD */
+
+#ifdef XML_DTD
+#define XML_N_STATES 4
+#else /* not XML_DTD */
+#define XML_N_STATES 3
+#endif /* not XML_DTD */
+
+#define XML_PROLOG_STATE 0
+#define XML_CONTENT_STATE 1
+#define XML_CDATA_SECTION_STATE 2
+#ifdef XML_DTD
+#define XML_IGNORE_SECTION_STATE 3
+#endif /* XML_DTD */
+
+#define XML_N_LITERAL_TYPES 2
+#define XML_ATTRIBUTE_VALUE_LITERAL 0
+#define XML_ENTITY_VALUE_LITERAL 1
+
+/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
+#define XML_UTF8_ENCODE_MAX 4
+/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
+#define XML_UTF16_ENCODE_MAX 2
+
+typedef struct position {
+  /* first line and first column are 0 not 1 */
+  XML_Size lineNumber;
+  XML_Size columnNumber;
+} POSITION;
+
+typedef struct {
+  const char *name;
+  const char *valuePtr;
+  const char *valueEnd;
+  char normalized;
+} ATTRIBUTE;
+
+struct encoding;
+typedef struct encoding ENCODING;
+
+typedef int (PTRCALL *SCANNER)(const ENCODING *,
+                               const char *,
+                               const char *,
+                               const char **);
+
+enum XML_Convert_Result {
+  XML_CONVERT_COMPLETED = 0,
+  XML_CONVERT_INPUT_INCOMPLETE = 1,
+  XML_CONVERT_OUTPUT_EXHAUSTED = 2  /* and therefore potentially input remaining as well */
+};
+
+struct encoding {
+  SCANNER scanners[XML_N_STATES];
+  SCANNER literalScanners[XML_N_LITERAL_TYPES];
+  int (PTRCALL *sameName)(const ENCODING *,
+                          const char *,
+                          const char *);
+  int (PTRCALL *nameMatchesAscii)(const ENCODING *,
+                                  const char *,
+                                  const char *,
+                                  const char *);
+  int (PTRFASTCALL *nameLength)(const ENCODING *, const char *);
+  const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *);
+  int (PTRCALL *getAtts)(const ENCODING *enc,
+                         const char *ptr,
+                         int attsMax,
+                         ATTRIBUTE *atts);
+  int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr);
+  int (PTRCALL *predefinedEntityName)(const ENCODING *,
+                                      const char *,
+                                      const char *);
+  void (PTRCALL *updatePosition)(const ENCODING *,
+                                 const char *ptr,
+                                 const char *end,
+                                 POSITION *);
+  int (PTRCALL *isPublicId)(const ENCODING *enc,
+                            const char *ptr,
+                            const char *end,
+                            const char **badPtr);
+  enum XML_Convert_Result (PTRCALL *utf8Convert)(const ENCODING *enc,
+                              const char **fromP,
+                              const char *fromLim,
+                              char **toP,
+                              const char *toLim);
+  enum XML_Convert_Result (PTRCALL *utf16Convert)(const ENCODING *enc,
+                               const char **fromP,
+                               const char *fromLim,
+                               unsigned short **toP,
+                               const unsigned short *toLim);
+  int minBytesPerChar;
+  char isUtf8;
+  char isUtf16;
+};
+
+/* Scan the string starting at ptr until the end of the next complete
+   token, but do not scan past eptr.  Return an integer giving the
+   type of token.
+
+   Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
+
+   Return XML_TOK_PARTIAL when the string does not contain a complete
+   token; nextTokPtr will not be set.
+
+   Return XML_TOK_INVALID when the string does not start a valid
+   token; nextTokPtr will be set to point to the character which made
+   the token invalid.
+
+   Otherwise the string starts with a valid token; nextTokPtr will be
+   set to point to the character following the end of that token.
+
+   Each data character counts as a single token, but adjacent data
+   characters may be returned together.  Similarly for characters in
+   the prolog outside literals, comments and processing instructions.
+*/
+
+
+#define XmlTok(enc, state, ptr, end, nextTokPtr) \
+  (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
+
+#define XmlPrologTok(enc, ptr, end, nextTokPtr) \
+   XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
+
+#define XmlContentTok(enc, ptr, end, nextTokPtr) \
+   XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
+
+#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
+   XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
+
+#ifdef XML_DTD
+
+#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \
+   XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
+
+#endif /* XML_DTD */
+
+/* This is used for performing a 2nd-level tokenization on the content
+   of a literal that has already been returned by XmlTok.
+*/
+#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
+  (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
+
+#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
+   XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
+   XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2))
+
+#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \
+  (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
+
+#define XmlNameLength(enc, ptr) \
+  (((enc)->nameLength)(enc, ptr))
+
+#define XmlSkipS(enc, ptr) \
+  (((enc)->skipS)(enc, ptr))
+
+#define XmlGetAttributes(enc, ptr, attsMax, atts) \
+  (((enc)->getAtts)(enc, ptr, attsMax, atts))
+
+#define XmlCharRefNumber(enc, ptr) \
+  (((enc)->charRefNumber)(enc, ptr))
+
+#define XmlPredefinedEntityName(enc, ptr, end) \
+  (((enc)->predefinedEntityName)(enc, ptr, end))
+
+#define XmlUpdatePosition(enc, ptr, end, pos) \
+  (((enc)->updatePosition)(enc, ptr, end, pos))
+
+#define XmlIsPublicId(enc, ptr, end, badPtr) \
+  (((enc)->isPublicId)(enc, ptr, end, badPtr))
+
+#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
+  (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
+
+#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
+  (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
+
+typedef struct {
+  ENCODING initEnc;
+  const ENCODING **encPtr;
+} INIT_ENCODING;
+
+int XmlParseXmlDecl(int isGeneralTextEntity,
+                    const ENCODING *enc,
+                    const char *ptr,
+                    const char *end,
+                    const char **badPtr,
+                    const char **versionPtr,
+                    const char **versionEndPtr,
+                    const char **encodingNamePtr,
+                    const ENCODING **namedEncodingPtr,
+                    int *standalonePtr);
+
+int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING *XmlGetUtf8InternalEncoding(void);
+const ENCODING *XmlGetUtf16InternalEncoding(void);
+int FASTCALL XmlUtf8Encode(int charNumber, char *buf);
+int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf);
+int XmlSizeOfUnknownEncoding(void);
+
+
+typedef int (XMLCALL *CONVERTER) (void *userData, const char *p);
+
+ENCODING *
+XmlInitUnknownEncoding(void *mem,
+                       int *table,
+                       CONVERTER convert,
+                       void *userData);
+
+int XmlParseXmlDeclNS(int isGeneralTextEntity,
+                      const ENCODING *enc,
+                      const char *ptr,
+                      const char *end,
+                      const char **badPtr,
+                      const char **versionPtr,
+                      const char **versionEndPtr,
+                      const char **encodingNamePtr,
+                      const ENCODING **namedEncodingPtr,
+                      int *standalonePtr);
+
+int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING *XmlGetUtf8InternalEncodingNS(void);
+const ENCODING *XmlGetUtf16InternalEncodingNS(void);
+ENCODING *
+XmlInitUnknownEncodingNS(void *mem,
+                         int *table,
+                         CONVERTER convert,
+                         void *userData);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlTok_INCLUDED */
diff --git a/deps/EXPAT/expat/xmltok_impl.h b/deps/EXPAT/expat/xmltok_impl.h
new file mode 100644
index 000000000..da0ea60a6
--- /dev/null
+++ b/deps/EXPAT/expat/xmltok_impl.h
@@ -0,0 +1,46 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file COPYING for copying permission.
+*/
+
+enum {
+  BT_NONXML,
+  BT_MALFORM,
+  BT_LT,
+  BT_AMP,
+  BT_RSQB,
+  BT_LEAD2,
+  BT_LEAD3,
+  BT_LEAD4,
+  BT_TRAIL,
+  BT_CR,
+  BT_LF,
+  BT_GT,
+  BT_QUOT,
+  BT_APOS,
+  BT_EQUALS,
+  BT_QUEST,
+  BT_EXCL,
+  BT_SOL,
+  BT_SEMI,
+  BT_NUM,
+  BT_LSQB,
+  BT_S,
+  BT_NMSTRT,
+  BT_COLON,
+  BT_HEX,
+  BT_DIGIT,
+  BT_NAME,
+  BT_MINUS,
+  BT_OTHER, /* known not to be a name or name start character */
+  BT_NONASCII, /* might be a name or name start character */
+  BT_PERCNT,
+  BT_LPAR,
+  BT_RPAR,
+  BT_AST,
+  BT_PLUS,
+  BT_COMMA,
+  BT_VERBAR
+};
+
+#include <stddef.h>
diff --git a/deps/EXPAT/expat/xmltok_impl.inc b/deps/EXPAT/expat/xmltok_impl.inc
new file mode 100644
index 000000000..5f779c057
--- /dev/null
+++ b/deps/EXPAT/expat/xmltok_impl.inc
@@ -0,0 +1,1779 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+/* This file is included! */
+#ifdef XML_TOK_IMPL_C
+
+#ifndef IS_INVALID_CHAR
+#define IS_INVALID_CHAR(enc, ptr, n) (0)
+#endif
+
+#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \
+    case BT_LEAD ## n: \
+      if (end - ptr < n) \
+        return XML_TOK_PARTIAL_CHAR; \
+      if (IS_INVALID_CHAR(enc, ptr, n)) { \
+        *(nextTokPtr) = (ptr); \
+        return XML_TOK_INVALID; \
+      } \
+      ptr += n; \
+      break;
+
+#define INVALID_CASES(ptr, nextTokPtr) \
+  INVALID_LEAD_CASE(2, ptr, nextTokPtr) \
+  INVALID_LEAD_CASE(3, ptr, nextTokPtr) \
+  INVALID_LEAD_CASE(4, ptr, nextTokPtr) \
+  case BT_NONXML: \
+  case BT_MALFORM: \
+  case BT_TRAIL: \
+    *(nextTokPtr) = (ptr); \
+    return XML_TOK_INVALID;
+
+#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \
+   case BT_LEAD ## n: \
+     if (end - ptr < n) \
+       return XML_TOK_PARTIAL_CHAR; \
+     if (!IS_NAME_CHAR(enc, ptr, n)) { \
+       *nextTokPtr = ptr; \
+       return XML_TOK_INVALID; \
+     } \
+     ptr += n; \
+     break;
+
+#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \
+  case BT_NONASCII: \
+    if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \
+      *nextTokPtr = ptr; \
+      return XML_TOK_INVALID; \
+    } \
+  case BT_NMSTRT: \
+  case BT_HEX: \
+  case BT_DIGIT: \
+  case BT_NAME: \
+  case BT_MINUS: \
+    ptr += MINBPC(enc); \
+    break; \
+  CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \
+  CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \
+  CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr)
+
+#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \
+   case BT_LEAD ## n: \
+     if (end - ptr < n) \
+       return XML_TOK_PARTIAL_CHAR; \
+     if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \
+       *nextTokPtr = ptr; \
+       return XML_TOK_INVALID; \
+     } \
+     ptr += n; \
+     break;
+
+#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \
+  case BT_NONASCII: \
+    if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \
+      *nextTokPtr = ptr; \
+      return XML_TOK_INVALID; \
+    } \
+  case BT_NMSTRT: \
+  case BT_HEX: \
+    ptr += MINBPC(enc); \
+    break; \
+  CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \
+  CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \
+  CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr)
+
+#ifndef PREFIX
+#define PREFIX(ident) ident
+#endif
+
+
+#define HAS_CHARS(enc, ptr, end, count) \
+    (end - ptr >= count * MINBPC(enc))
+
+#define HAS_CHAR(enc, ptr, end) \
+    HAS_CHARS(enc, ptr, end, 1)
+
+#define REQUIRE_CHARS(enc, ptr, end, count) \
+    { \
+      if (! HAS_CHARS(enc, ptr, end, count)) { \
+        return XML_TOK_PARTIAL; \
+      } \
+    }
+
+#define REQUIRE_CHAR(enc, ptr, end) \
+    REQUIRE_CHARS(enc, ptr, end, 1)
+
+
+/* ptr points to character following "<!-" */
+
+static int PTRCALL
+PREFIX(scanComment)(const ENCODING *enc, const char *ptr,
+                    const char *end, const char **nextTokPtr)
+{
+  if (HAS_CHAR(enc, ptr, end)) {
+    if (!CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+    ptr += MINBPC(enc);
+    while (HAS_CHAR(enc, ptr, end)) {
+      switch (BYTE_TYPE(enc, ptr)) {
+      INVALID_CASES(ptr, nextTokPtr)
+      case BT_MINUS:
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        if (CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
+          ptr += MINBPC(enc);
+          REQUIRE_CHAR(enc, ptr, end);
+          if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+          }
+          *nextTokPtr = ptr + MINBPC(enc);
+          return XML_TOK_COMMENT;
+        }
+        break;
+      default:
+        ptr += MINBPC(enc);
+        break;
+      }
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<!" */
+
+static int PTRCALL
+PREFIX(scanDecl)(const ENCODING *enc, const char *ptr,
+                 const char *end, const char **nextTokPtr)
+{
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+  case BT_MINUS:
+    return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_LSQB:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_COND_SECT_OPEN;
+  case BT_NMSTRT:
+  case BT_HEX:
+    ptr += MINBPC(enc);
+    break;
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_PERCNT:
+      REQUIRE_CHARS(enc, ptr, end, 2);
+      /* don't allow <!ENTITY% foo "whatever"> */
+      switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) {
+      case BT_S: case BT_CR: case BT_LF: case BT_PERCNT:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      /* fall through */
+    case BT_S: case BT_CR: case BT_LF:
+      *nextTokPtr = ptr;
+      return XML_TOK_DECL_OPEN;
+    case BT_NMSTRT:
+    case BT_HEX:
+      ptr += MINBPC(enc);
+      break;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(checkPiTarget)(const ENCODING *UNUSED_P(enc), const char *ptr,
+                      const char *end, int *tokPtr)
+{
+  int upper = 0;
+  *tokPtr = XML_TOK_PI;
+  if (end - ptr != MINBPC(enc)*3)
+    return 1;
+  switch (BYTE_TO_ASCII(enc, ptr)) {
+  case ASCII_x:
+    break;
+  case ASCII_X:
+    upper = 1;
+    break;
+  default:
+    return 1;
+  }
+  ptr += MINBPC(enc);
+  switch (BYTE_TO_ASCII(enc, ptr)) {
+  case ASCII_m:
+    break;
+  case ASCII_M:
+    upper = 1;
+    break;
+  default:
+    return 1;
+  }
+  ptr += MINBPC(enc);
+  switch (BYTE_TO_ASCII(enc, ptr)) {
+  case ASCII_l:
+    break;
+  case ASCII_L:
+    upper = 1;
+    break;
+  default:
+    return 1;
+  }
+  if (upper)
+    return 0;
+  *tokPtr = XML_TOK_XML_DECL;
+  return 1;
+}
+
+/* ptr points to character following "<?" */
+
+static int PTRCALL
+PREFIX(scanPi)(const ENCODING *enc, const char *ptr,
+               const char *end, const char **nextTokPtr)
+{
+  int tok;
+  const char *target = ptr;
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+  CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_S: case BT_CR: case BT_LF:
+      if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      ptr += MINBPC(enc);
+      while (HAS_CHAR(enc, ptr, end)) {
+        switch (BYTE_TYPE(enc, ptr)) {
+        INVALID_CASES(ptr, nextTokPtr)
+        case BT_QUEST:
+          ptr += MINBPC(enc);
+          REQUIRE_CHAR(enc, ptr, end);
+          if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+            *nextTokPtr = ptr + MINBPC(enc);
+            return tok;
+          }
+          break;
+        default:
+          ptr += MINBPC(enc);
+          break;
+        }
+      }
+      return XML_TOK_PARTIAL;
+    case BT_QUEST:
+      if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+        *nextTokPtr = ptr + MINBPC(enc);
+        return tok;
+      }
+      /* fall through */
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(scanCdataSection)(const ENCODING *UNUSED_P(enc), const char *ptr,
+                         const char *end, const char **nextTokPtr)
+{
+  static const char CDATA_LSQB[] = { ASCII_C, ASCII_D, ASCII_A,
+                                     ASCII_T, ASCII_A, ASCII_LSQB };
+  int i;
+  /* CDATA[ */
+  REQUIRE_CHARS(enc, ptr, end, 6);
+  for (i = 0; i < 6; i++, ptr += MINBPC(enc)) {
+    if (!CHAR_MATCHES(enc, ptr, CDATA_LSQB[i])) {
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_CDATA_SECT_OPEN;
+}
+
+static int PTRCALL
+PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr,
+                        const char *end, const char **nextTokPtr)
+{
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  if (MINBPC(enc) > 1) {
+    size_t n = end - ptr;
+    if (n & (MINBPC(enc) - 1)) {
+      n &= ~(MINBPC(enc) - 1);
+      if (n == 0)
+        return XML_TOK_PARTIAL;
+      end = ptr + n;
+    }
+  }
+  switch (BYTE_TYPE(enc, ptr)) {
+  case BT_RSQB:
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB))
+      break;
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+      ptr -= MINBPC(enc);
+      break;
+    }
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_CDATA_SECT_CLOSE;
+  case BT_CR:
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    if (BYTE_TYPE(enc, ptr) == BT_LF)
+      ptr += MINBPC(enc);
+    *nextTokPtr = ptr;
+    return XML_TOK_DATA_NEWLINE;
+  case BT_LF:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_DATA_NEWLINE;
+  INVALID_CASES(ptr, nextTokPtr)
+  default:
+    ptr += MINBPC(enc);
+    break;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: \
+      if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+        *nextTokPtr = ptr; \
+        return XML_TOK_DATA_CHARS; \
+      } \
+      ptr += n; \
+      break;
+    LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+    case BT_NONXML:
+    case BT_MALFORM:
+    case BT_TRAIL:
+    case BT_CR:
+    case BT_LF:
+    case BT_RSQB:
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "</" */
+
+static int PTRCALL
+PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr,
+                   const char *end, const char **nextTokPtr)
+{
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+  CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_S: case BT_CR: case BT_LF:
+      for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_S: case BT_CR: case BT_LF:
+          break;
+        case BT_GT:
+          *nextTokPtr = ptr + MINBPC(enc);
+          return XML_TOK_END_TAG;
+        default:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        }
+      }
+      return XML_TOK_PARTIAL;
+#ifdef XML_NS
+    case BT_COLON:
+      /* no need to check qname syntax here,
+         since end-tag must match exactly */
+      ptr += MINBPC(enc);
+      break;
+#endif
+    case BT_GT:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_END_TAG;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#X" */
+
+static int PTRCALL
+PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr,
+                       const char *end, const char **nextTokPtr)
+{
+  if (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_DIGIT:
+    case BT_HEX:
+      break;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+    for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_DIGIT:
+      case BT_HEX:
+        break;
+      case BT_SEMI:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_CHAR_REF;
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#" */
+
+static int PTRCALL
+PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr,
+                    const char *end, const char **nextTokPtr)
+{
+  if (HAS_CHAR(enc, ptr, end)) {
+    if (CHAR_MATCHES(enc, ptr, ASCII_x))
+      return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_DIGIT:
+      break;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+    for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_DIGIT:
+        break;
+      case BT_SEMI:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_CHAR_REF;
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&" */
+
+static int PTRCALL
+PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end,
+                const char **nextTokPtr)
+{
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+  CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  case BT_NUM:
+    return PREFIX(scanCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_SEMI:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_ENTITY_REF;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following first character of attribute name */
+
+static int PTRCALL
+PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end,
+                 const char **nextTokPtr)
+{
+#ifdef XML_NS
+  int hadColon = 0;
+#endif
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+    case BT_COLON:
+      if (hadColon) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      hadColon = 1;
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      break;
+#endif
+    case BT_S: case BT_CR: case BT_LF:
+      for (;;) {
+        int t;
+
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        t = BYTE_TYPE(enc, ptr);
+        if (t == BT_EQUALS)
+          break;
+        switch (t) {
+        case BT_S:
+        case BT_LF:
+        case BT_CR:
+          break;
+        default:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        }
+      }
+    /* fall through */
+    case BT_EQUALS:
+      {
+        int open;
+#ifdef XML_NS
+        hadColon = 0;
+#endif
+        for (;;) {
+          ptr += MINBPC(enc);
+          REQUIRE_CHAR(enc, ptr, end);
+          open = BYTE_TYPE(enc, ptr);
+          if (open == BT_QUOT || open == BT_APOS)
+            break;
+          switch (open) {
+          case BT_S:
+          case BT_LF:
+          case BT_CR:
+            break;
+          default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+          }
+        }
+        ptr += MINBPC(enc);
+        /* in attribute value */
+        for (;;) {
+          int t;
+          REQUIRE_CHAR(enc, ptr, end);
+          t = BYTE_TYPE(enc, ptr);
+          if (t == open)
+            break;
+          switch (t) {
+          INVALID_CASES(ptr, nextTokPtr)
+          case BT_AMP:
+            {
+              int tok = PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, &ptr);
+              if (tok <= 0) {
+                if (tok == XML_TOK_INVALID)
+                  *nextTokPtr = ptr;
+                return tok;
+              }
+              break;
+            }
+          case BT_LT:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+          default:
+            ptr += MINBPC(enc);
+            break;
+          }
+        }
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_S:
+        case BT_CR:
+        case BT_LF:
+          break;
+        case BT_SOL:
+          goto sol;
+        case BT_GT:
+          goto gt;
+        default:
+          *nextTokPtr = ptr;
+          return XML_TOK_INVALID;
+        }
+        /* ptr points to closing quote */
+        for (;;) {
+          ptr += MINBPC(enc);
+          REQUIRE_CHAR(enc, ptr, end);
+          switch (BYTE_TYPE(enc, ptr)) {
+          CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+          case BT_S: case BT_CR: case BT_LF:
+            continue;
+          case BT_GT:
+          gt:
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_START_TAG_WITH_ATTS;
+          case BT_SOL:
+          sol:
+            ptr += MINBPC(enc);
+            REQUIRE_CHAR(enc, ptr, end);
+            if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+              *nextTokPtr = ptr;
+              return XML_TOK_INVALID;
+            }
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_EMPTY_ELEMENT_WITH_ATTS;
+          default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+          }
+          break;
+        }
+        break;
+      }
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<" */
+
+static int PTRCALL
+PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end,
+               const char **nextTokPtr)
+{
+#ifdef XML_NS
+  int hadColon;
+#endif
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+  CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  case BT_EXCL:
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_MINUS:
+      return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_LSQB:
+      return PREFIX(scanCdataSection)(enc, ptr + MINBPC(enc),
+                                      end, nextTokPtr);
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  case BT_QUEST:
+    return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_SOL:
+    return PREFIX(scanEndTag)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+#ifdef XML_NS
+  hadColon = 0;
+#endif
+  /* we have a start-tag */
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+    case BT_COLON:
+      if (hadColon) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      hadColon = 1;
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      switch (BYTE_TYPE(enc, ptr)) {
+      CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      break;
+#endif
+    case BT_S: case BT_CR: case BT_LF:
+      {
+        ptr += MINBPC(enc);
+        while (HAS_CHAR(enc, ptr, end)) {
+          switch (BYTE_TYPE(enc, ptr)) {
+          CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+          case BT_GT:
+            goto gt;
+          case BT_SOL:
+            goto sol;
+          case BT_S: case BT_CR: case BT_LF:
+            ptr += MINBPC(enc);
+            continue;
+          default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+          }
+          return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr);
+        }
+        return XML_TOK_PARTIAL;
+      }
+    case BT_GT:
+    gt:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_START_TAG_NO_ATTS;
+    case BT_SOL:
+    sol:
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_EMPTY_ELEMENT_NO_ATTS;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **nextTokPtr)
+{
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  if (MINBPC(enc) > 1) {
+    size_t n = end - ptr;
+    if (n & (MINBPC(enc) - 1)) {
+      n &= ~(MINBPC(enc) - 1);
+      if (n == 0)
+        return XML_TOK_PARTIAL;
+      end = ptr + n;
+    }
+  }
+  switch (BYTE_TYPE(enc, ptr)) {
+  case BT_LT:
+    return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_AMP:
+    return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_CR:
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return XML_TOK_TRAILING_CR;
+    if (BYTE_TYPE(enc, ptr) == BT_LF)
+      ptr += MINBPC(enc);
+    *nextTokPtr = ptr;
+    return XML_TOK_DATA_NEWLINE;
+  case BT_LF:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_DATA_NEWLINE;
+  case BT_RSQB:
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return XML_TOK_TRAILING_RSQB;
+    if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB))
+      break;
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return XML_TOK_TRAILING_RSQB;
+    if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+      ptr -= MINBPC(enc);
+      break;
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  INVALID_CASES(ptr, nextTokPtr)
+  default:
+    ptr += MINBPC(enc);
+    break;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: \
+      if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+        *nextTokPtr = ptr; \
+        return XML_TOK_DATA_CHARS; \
+      } \
+      ptr += n; \
+      break;
+    LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+    case BT_RSQB:
+      if (HAS_CHARS(enc, ptr, end, 2)) {
+         if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) {
+           ptr += MINBPC(enc);
+           break;
+         }
+         if (HAS_CHARS(enc, ptr, end, 3)) {
+           if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) {
+             ptr += MINBPC(enc);
+             break;
+           }
+           *nextTokPtr = ptr + 2*MINBPC(enc);
+           return XML_TOK_INVALID;
+         }
+      }
+      /* fall through */
+    case BT_AMP:
+    case BT_LT:
+    case BT_NONXML:
+    case BT_MALFORM:
+    case BT_TRAIL:
+    case BT_CR:
+    case BT_LF:
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "%" */
+
+static int PTRCALL
+PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end,
+                    const char **nextTokPtr)
+{
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+  CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  case BT_S: case BT_LF: case BT_CR: case BT_PERCNT:
+    *nextTokPtr = ptr;
+    return XML_TOK_PERCENT;
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_SEMI:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_PARAM_ENTITY_REF;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end,
+                      const char **nextTokPtr)
+{
+  REQUIRE_CHAR(enc, ptr, end);
+  switch (BYTE_TYPE(enc, ptr)) {
+  CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_CR: case BT_LF: case BT_S:
+    case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR:
+      *nextTokPtr = ptr;
+      return XML_TOK_POUND_NAME;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return -XML_TOK_POUND_NAME;
+}
+
+static int PTRCALL
+PREFIX(scanLit)(int open, const ENCODING *enc,
+                const char *ptr, const char *end,
+                const char **nextTokPtr)
+{
+  while (HAS_CHAR(enc, ptr, end)) {
+    int t = BYTE_TYPE(enc, ptr);
+    switch (t) {
+    INVALID_CASES(ptr, nextTokPtr)
+    case BT_QUOT:
+    case BT_APOS:
+      ptr += MINBPC(enc);
+      if (t != open)
+        break;
+      if (! HAS_CHAR(enc, ptr, end))
+        return -XML_TOK_LITERAL;
+      *nextTokPtr = ptr;
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_S: case BT_CR: case BT_LF:
+      case BT_GT: case BT_PERCNT: case BT_LSQB:
+        return XML_TOK_LITERAL;
+      default:
+        return XML_TOK_INVALID;
+      }
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+static int PTRCALL
+PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
+                  const char **nextTokPtr)
+{
+  int tok;
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  if (MINBPC(enc) > 1) {
+    size_t n = end - ptr;
+    if (n & (MINBPC(enc) - 1)) {
+      n &= ~(MINBPC(enc) - 1);
+      if (n == 0)
+        return XML_TOK_PARTIAL;
+      end = ptr + n;
+    }
+  }
+  switch (BYTE_TYPE(enc, ptr)) {
+  case BT_QUOT:
+    return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_APOS:
+    return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_LT:
+    {
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_EXCL:
+        return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+      case BT_QUEST:
+        return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+      case BT_NMSTRT:
+      case BT_HEX:
+      case BT_NONASCII:
+      case BT_LEAD2:
+      case BT_LEAD3:
+      case BT_LEAD4:
+        *nextTokPtr = ptr - MINBPC(enc);
+        return XML_TOK_INSTANCE_START;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  case BT_CR:
+    if (ptr + MINBPC(enc) == end) {
+      *nextTokPtr = end;
+      /* indicate that this might be part of a CR/LF pair */
+      return -XML_TOK_PROLOG_S;
+    }
+    /* fall through */
+  case BT_S: case BT_LF:
+    for (;;) {
+      ptr += MINBPC(enc);
+      if (! HAS_CHAR(enc, ptr, end))
+        break;
+      switch (BYTE_TYPE(enc, ptr)) {
+      case BT_S: case BT_LF:
+        break;
+      case BT_CR:
+        /* don't split CR/LF pair */
+        if (ptr + MINBPC(enc) != end)
+          break;
+        /* fall through */
+      default:
+        *nextTokPtr = ptr;
+        return XML_TOK_PROLOG_S;
+      }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_PROLOG_S;
+  case BT_PERCNT:
+    return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+  case BT_COMMA:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_COMMA;
+  case BT_LSQB:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_OPEN_BRACKET;
+  case BT_RSQB:
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return -XML_TOK_CLOSE_BRACKET;
+    if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
+      REQUIRE_CHARS(enc, ptr, end, 2);
+      if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) {
+        *nextTokPtr = ptr + 2*MINBPC(enc);
+        return XML_TOK_COND_SECT_CLOSE;
+      }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_CLOSE_BRACKET;
+  case BT_LPAR:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_OPEN_PAREN;
+  case BT_RPAR:
+    ptr += MINBPC(enc);
+    if (! HAS_CHAR(enc, ptr, end))
+      return -XML_TOK_CLOSE_PAREN;
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_AST:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_CLOSE_PAREN_ASTERISK;
+    case BT_QUEST:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_CLOSE_PAREN_QUESTION;
+    case BT_PLUS:
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_CLOSE_PAREN_PLUS;
+    case BT_CR: case BT_LF: case BT_S:
+    case BT_GT: case BT_COMMA: case BT_VERBAR:
+    case BT_RPAR:
+      *nextTokPtr = ptr;
+      return XML_TOK_CLOSE_PAREN;
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  case BT_VERBAR:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_OR;
+  case BT_GT:
+    *nextTokPtr = ptr + MINBPC(enc);
+    return XML_TOK_DECL_CLOSE;
+  case BT_NUM:
+    return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+#define LEAD_CASE(n) \
+  case BT_LEAD ## n: \
+    if (end - ptr < n) \
+      return XML_TOK_PARTIAL_CHAR; \
+    if (IS_NMSTRT_CHAR(enc, ptr, n)) { \
+      ptr += n; \
+      tok = XML_TOK_NAME; \
+      break; \
+    } \
+    if (IS_NAME_CHAR(enc, ptr, n)) { \
+      ptr += n; \
+      tok = XML_TOK_NMTOKEN; \
+      break; \
+    } \
+    *nextTokPtr = ptr; \
+    return XML_TOK_INVALID;
+    LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+  case BT_NMSTRT:
+  case BT_HEX:
+    tok = XML_TOK_NAME;
+    ptr += MINBPC(enc);
+    break;
+  case BT_DIGIT:
+  case BT_NAME:
+  case BT_MINUS:
+#ifdef XML_NS
+  case BT_COLON:
+#endif
+    tok = XML_TOK_NMTOKEN;
+    ptr += MINBPC(enc);
+    break;
+  case BT_NONASCII:
+    if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) {
+      ptr += MINBPC(enc);
+      tok = XML_TOK_NAME;
+      break;
+    }
+    if (IS_NAME_CHAR_MINBPC(enc, ptr)) {
+      ptr += MINBPC(enc);
+      tok = XML_TOK_NMTOKEN;
+      break;
+    }
+    /* fall through */
+  default:
+    *nextTokPtr = ptr;
+    return XML_TOK_INVALID;
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+    case BT_GT: case BT_RPAR: case BT_COMMA:
+    case BT_VERBAR: case BT_LSQB: case BT_PERCNT:
+    case BT_S: case BT_CR: case BT_LF:
+      *nextTokPtr = ptr;
+      return tok;
+#ifdef XML_NS
+    case BT_COLON:
+      ptr += MINBPC(enc);
+      switch (tok) {
+      case XML_TOK_NAME:
+        REQUIRE_CHAR(enc, ptr, end);
+        tok = XML_TOK_PREFIXED_NAME;
+        switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+        default:
+          tok = XML_TOK_NMTOKEN;
+          break;
+        }
+        break;
+      case XML_TOK_PREFIXED_NAME:
+        tok = XML_TOK_NMTOKEN;
+        break;
+      }
+      break;
+#endif
+    case BT_PLUS:
+      if (tok == XML_TOK_NMTOKEN)  {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_NAME_PLUS;
+    case BT_AST:
+      if (tok == XML_TOK_NMTOKEN)  {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_NAME_ASTERISK;
+    case BT_QUEST:
+      if (tok == XML_TOK_NMTOKEN)  {
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+      }
+      *nextTokPtr = ptr + MINBPC(enc);
+      return XML_TOK_NAME_QUESTION;
+    default:
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    }
+  }
+  return -tok;
+}
+
+static int PTRCALL
+PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr,
+                          const char *end, const char **nextTokPtr)
+{
+  const char *start;
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  else if (! HAS_CHAR(enc, ptr, end))
+    return XML_TOK_PARTIAL;
+  start = ptr;
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: ptr += n; break;
+    LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+    case BT_AMP:
+      if (ptr == start)
+        return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_LT:
+      /* this is for inside entity references */
+      *nextTokPtr = ptr;
+      return XML_TOK_INVALID;
+    case BT_LF:
+      if (ptr == start) {
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_DATA_NEWLINE;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_CR:
+      if (ptr == start) {
+        ptr += MINBPC(enc);
+        if (! HAS_CHAR(enc, ptr, end))
+          return XML_TOK_TRAILING_CR;
+        if (BYTE_TYPE(enc, ptr) == BT_LF)
+          ptr += MINBPC(enc);
+        *nextTokPtr = ptr;
+        return XML_TOK_DATA_NEWLINE;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_S:
+      if (ptr == start) {
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_ATTRIBUTE_VALUE_S;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_DATA_CHARS;
+}
+
+static int PTRCALL
+PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr,
+                       const char *end, const char **nextTokPtr)
+{
+  const char *start;
+  if (ptr >= end)
+    return XML_TOK_NONE;
+  else if (! HAS_CHAR(enc, ptr, end))
+    return XML_TOK_PARTIAL;
+  start = ptr;
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: ptr += n; break;
+    LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+    case BT_AMP:
+      if (ptr == start)
+        return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_PERCNT:
+      if (ptr == start) {
+        int tok =  PREFIX(scanPercent)(enc, ptr + MINBPC(enc),
+                                       end, nextTokPtr);
+        return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_LF:
+      if (ptr == start) {
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_DATA_NEWLINE;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    case BT_CR:
+      if (ptr == start) {
+        ptr += MINBPC(enc);
+        if (! HAS_CHAR(enc, ptr, end))
+          return XML_TOK_TRAILING_CR;
+        if (BYTE_TYPE(enc, ptr) == BT_LF)
+          ptr += MINBPC(enc);
+        *nextTokPtr = ptr;
+        return XML_TOK_DATA_NEWLINE;
+      }
+      *nextTokPtr = ptr;
+      return XML_TOK_DATA_CHARS;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  *nextTokPtr = ptr;
+  return XML_TOK_DATA_CHARS;
+}
+
+#ifdef XML_DTD
+
+static int PTRCALL
+PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr,
+                         const char *end, const char **nextTokPtr)
+{
+  int level = 0;
+  if (MINBPC(enc) > 1) {
+    size_t n = end - ptr;
+    if (n & (MINBPC(enc) - 1)) {
+      n &= ~(MINBPC(enc) - 1);
+      end = ptr + n;
+    }
+  }
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    INVALID_CASES(ptr, nextTokPtr)
+    case BT_LT:
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) {
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) {
+          ++level;
+          ptr += MINBPC(enc);
+        }
+      }
+      break;
+    case BT_RSQB:
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
+      if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
+        if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+          ptr += MINBPC(enc);
+          if (level == 0) {
+            *nextTokPtr = ptr;
+            return XML_TOK_IGNORE_SECT;
+          }
+          --level;
+        }
+      }
+      break;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+  }
+  return XML_TOK_PARTIAL;
+}
+
+#endif /* XML_DTD */
+
+static int PTRCALL
+PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **badPtr)
+{
+  ptr += MINBPC(enc);
+  end -= MINBPC(enc);
+  for (; HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_DIGIT:
+    case BT_HEX:
+    case BT_MINUS:
+    case BT_APOS:
+    case BT_LPAR:
+    case BT_RPAR:
+    case BT_PLUS:
+    case BT_COMMA:
+    case BT_SOL:
+    case BT_EQUALS:
+    case BT_QUEST:
+    case BT_CR:
+    case BT_LF:
+    case BT_SEMI:
+    case BT_EXCL:
+    case BT_AST:
+    case BT_PERCNT:
+    case BT_NUM:
+#ifdef XML_NS
+    case BT_COLON:
+#endif
+      break;
+    case BT_S:
+      if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) {
+        *badPtr = ptr;
+        return 0;
+      }
+      break;
+    case BT_NAME:
+    case BT_NMSTRT:
+      if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f))
+        break;
+    default:
+      switch (BYTE_TO_ASCII(enc, ptr)) {
+      case 0x24: /* $ */
+      case 0x40: /* @ */
+        break;
+      default:
+        *badPtr = ptr;
+        return 0;
+      }
+      break;
+    }
+  }
+  return 1;
+}
+
+/* This must only be called for a well-formed start-tag or empty
+   element tag.  Returns the number of attributes.  Pointers to the
+   first attsMax attributes are stored in atts.
+*/
+
+static int PTRCALL
+PREFIX(getAtts)(const ENCODING *enc, const char *ptr,
+                int attsMax, ATTRIBUTE *atts)
+{
+  enum { other, inName, inValue } state = inName;
+  int nAtts = 0;
+  int open = 0; /* defined when state == inValue;
+                   initialization just to shut up compilers */
+
+  for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#define START_NAME \
+      if (state == other) { \
+        if (nAtts < attsMax) { \
+          atts[nAtts].name = ptr; \
+          atts[nAtts].normalized = 1; \
+        } \
+        state = inName; \
+      }
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break;
+    LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+    case BT_NONASCII:
+    case BT_NMSTRT:
+    case BT_HEX:
+      START_NAME
+      break;
+#undef START_NAME
+    case BT_QUOT:
+      if (state != inValue) {
+        if (nAtts < attsMax)
+          atts[nAtts].valuePtr = ptr + MINBPC(enc);
+        state = inValue;
+        open = BT_QUOT;
+      }
+      else if (open == BT_QUOT) {
+        state = other;
+        if (nAtts < attsMax)
+          atts[nAtts].valueEnd = ptr;
+        nAtts++;
+      }
+      break;
+    case BT_APOS:
+      if (state != inValue) {
+        if (nAtts < attsMax)
+          atts[nAtts].valuePtr = ptr + MINBPC(enc);
+        state = inValue;
+        open = BT_APOS;
+      }
+      else if (open == BT_APOS) {
+        state = other;
+        if (nAtts < attsMax)
+          atts[nAtts].valueEnd = ptr;
+        nAtts++;
+      }
+      break;
+    case BT_AMP:
+      if (nAtts < attsMax)
+        atts[nAtts].normalized = 0;
+      break;
+    case BT_S:
+      if (state == inName)
+        state = other;
+      else if (state == inValue
+               && nAtts < attsMax
+               && atts[nAtts].normalized
+               && (ptr == atts[nAtts].valuePtr
+                   || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE
+                   || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE
+                   || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open))
+        atts[nAtts].normalized = 0;
+      break;
+    case BT_CR: case BT_LF:
+      /* This case ensures that the first attribute name is counted
+         Apart from that we could just change state on the quote. */
+      if (state == inName)
+        state = other;
+      else if (state == inValue && nAtts < attsMax)
+        atts[nAtts].normalized = 0;
+      break;
+    case BT_GT:
+    case BT_SOL:
+      if (state != inValue)
+        return nAtts;
+      break;
+    default:
+      break;
+    }
+  }
+  /* not reached */
+}
+
+static int PTRFASTCALL
+PREFIX(charRefNumber)(const ENCODING *UNUSED_P(enc), const char *ptr)
+{
+  int result = 0;
+  /* skip &# */
+  ptr += 2*MINBPC(enc);
+  if (CHAR_MATCHES(enc, ptr, ASCII_x)) {
+    for (ptr += MINBPC(enc);
+         !CHAR_MATCHES(enc, ptr, ASCII_SEMI);
+         ptr += MINBPC(enc)) {
+      int c = BYTE_TO_ASCII(enc, ptr);
+      switch (c) {
+      case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4:
+      case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9:
+        result <<= 4;
+        result |= (c - ASCII_0);
+        break;
+      case ASCII_A: case ASCII_B: case ASCII_C:
+      case ASCII_D: case ASCII_E: case ASCII_F:
+        result <<= 4;
+        result += 10 + (c - ASCII_A);
+        break;
+      case ASCII_a: case ASCII_b: case ASCII_c:
+      case ASCII_d: case ASCII_e: case ASCII_f:
+        result <<= 4;
+        result += 10 + (c - ASCII_a);
+        break;
+      }
+      if (result >= 0x110000)
+        return -1;
+    }
+  }
+  else {
+    for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) {
+      int c = BYTE_TO_ASCII(enc, ptr);
+      result *= 10;
+      result += (c - ASCII_0);
+      if (result >= 0x110000)
+        return -1;
+    }
+  }
+  return checkCharRefNumber(result);
+}
+
+static int PTRCALL
+PREFIX(predefinedEntityName)(const ENCODING *UNUSED_P(enc), const char *ptr,
+                             const char *end)
+{
+  switch ((end - ptr)/MINBPC(enc)) {
+  case 2:
+    if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) {
+      switch (BYTE_TO_ASCII(enc, ptr)) {
+      case ASCII_l:
+        return ASCII_LT;
+      case ASCII_g:
+        return ASCII_GT;
+      }
+    }
+    break;
+  case 3:
+    if (CHAR_MATCHES(enc, ptr, ASCII_a)) {
+      ptr += MINBPC(enc);
+      if (CHAR_MATCHES(enc, ptr, ASCII_m)) {
+        ptr += MINBPC(enc);
+        if (CHAR_MATCHES(enc, ptr, ASCII_p))
+          return ASCII_AMP;
+      }
+    }
+    break;
+  case 4:
+    switch (BYTE_TO_ASCII(enc, ptr)) {
+    case ASCII_q:
+      ptr += MINBPC(enc);
+      if (CHAR_MATCHES(enc, ptr, ASCII_u)) {
+        ptr += MINBPC(enc);
+        if (CHAR_MATCHES(enc, ptr, ASCII_o)) {
+          ptr += MINBPC(enc);
+          if (CHAR_MATCHES(enc, ptr, ASCII_t))
+            return ASCII_QUOT;
+        }
+      }
+      break;
+    case ASCII_a:
+      ptr += MINBPC(enc);
+      if (CHAR_MATCHES(enc, ptr, ASCII_p)) {
+        ptr += MINBPC(enc);
+        if (CHAR_MATCHES(enc, ptr, ASCII_o)) {
+          ptr += MINBPC(enc);
+          if (CHAR_MATCHES(enc, ptr, ASCII_s))
+            return ASCII_APOS;
+        }
+      }
+      break;
+    }
+  }
+  return 0;
+}
+
+static int PTRCALL
+PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2)
+{
+  for (;;) {
+    switch (BYTE_TYPE(enc, ptr1)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: \
+      if (*ptr1++ != *ptr2++) \
+        return 0;
+    LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2)
+#undef LEAD_CASE
+      /* fall through */
+      if (*ptr1++ != *ptr2++)
+        return 0;
+      break;
+    case BT_NONASCII:
+    case BT_NMSTRT:
+#ifdef XML_NS
+    case BT_COLON:
+#endif
+    case BT_HEX:
+    case BT_DIGIT:
+    case BT_NAME:
+    case BT_MINUS:
+      if (*ptr2++ != *ptr1++)
+        return 0;
+      if (MINBPC(enc) > 1) {
+        if (*ptr2++ != *ptr1++)
+          return 0;
+        if (MINBPC(enc) > 2) {
+          if (*ptr2++ != *ptr1++)
+            return 0;
+          if (MINBPC(enc) > 3) {
+            if (*ptr2++ != *ptr1++)
+              return 0;
+          }
+        }
+      }
+      break;
+    default:
+      if (MINBPC(enc) == 1 && *ptr1 == *ptr2)
+        return 1;
+      switch (BYTE_TYPE(enc, ptr2)) {
+      case BT_LEAD2:
+      case BT_LEAD3:
+      case BT_LEAD4:
+      case BT_NONASCII:
+      case BT_NMSTRT:
+#ifdef XML_NS
+      case BT_COLON:
+#endif
+      case BT_HEX:
+      case BT_DIGIT:
+      case BT_NAME:
+      case BT_MINUS:
+        return 0;
+      default:
+        return 1;
+      }
+    }
+  }
+  /* not reached */
+}
+
+static int PTRCALL
+PREFIX(nameMatchesAscii)(const ENCODING *UNUSED_P(enc), const char *ptr1,
+                         const char *end1, const char *ptr2)
+{
+  for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
+    if (end1 - ptr1 < MINBPC(enc))
+      return 0;
+    if (!CHAR_MATCHES(enc, ptr1, *ptr2))
+      return 0;
+  }
+  return ptr1 == end1;
+}
+
+static int PTRFASTCALL
+PREFIX(nameLength)(const ENCODING *enc, const char *ptr)
+{
+  const char *start = ptr;
+  for (;;) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: ptr += n; break;
+    LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+    case BT_NONASCII:
+    case BT_NMSTRT:
+#ifdef XML_NS
+    case BT_COLON:
+#endif
+    case BT_HEX:
+    case BT_DIGIT:
+    case BT_NAME:
+    case BT_MINUS:
+      ptr += MINBPC(enc);
+      break;
+    default:
+      return (int)(ptr - start);
+    }
+  }
+}
+
+static const char * PTRFASTCALL
+PREFIX(skipS)(const ENCODING *enc, const char *ptr)
+{
+  for (;;) {
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_LF:
+    case BT_CR:
+    case BT_S:
+      ptr += MINBPC(enc);
+      break;
+    default:
+      return ptr;
+    }
+  }
+}
+
+static void PTRCALL
+PREFIX(updatePosition)(const ENCODING *enc,
+                       const char *ptr,
+                       const char *end,
+                       POSITION *pos)
+{
+  while (HAS_CHAR(enc, ptr, end)) {
+    switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: \
+      ptr += n; \
+      break;
+    LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+    case BT_LF:
+      pos->columnNumber = (XML_Size)-1;
+      pos->lineNumber++;
+      ptr += MINBPC(enc);
+      break;
+    case BT_CR:
+      pos->lineNumber++;
+      ptr += MINBPC(enc);
+      if (HAS_CHAR(enc, ptr, end) && BYTE_TYPE(enc, ptr) == BT_LF)
+        ptr += MINBPC(enc);
+      pos->columnNumber = (XML_Size)-1;
+      break;
+    default:
+      ptr += MINBPC(enc);
+      break;
+    }
+    pos->columnNumber++;
+  }
+}
+
+#undef DO_LEAD_CASE
+#undef MULTIBYTE_CASES
+#undef INVALID_CASES
+#undef CHECK_NAME_CASE
+#undef CHECK_NAME_CASES
+#undef CHECK_NMSTRT_CASE
+#undef CHECK_NMSTRT_CASES
+
+#endif /* XML_TOK_IMPL_C */
diff --git a/deps/EXPAT/expat/xmltok_ns.inc b/deps/EXPAT/expat/xmltok_ns.inc
new file mode 100644
index 000000000..c3b88fdf4
--- /dev/null
+++ b/deps/EXPAT/expat/xmltok_ns.inc
@@ -0,0 +1,115 @@
+/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+   See the file COPYING for copying permission.
+*/
+
+/* This file is included! */
+#ifdef XML_TOK_NS_C
+
+const ENCODING *
+NS(XmlGetUtf8InternalEncoding)(void)
+{
+  return &ns(internal_utf8_encoding).enc;
+}
+
+const ENCODING *
+NS(XmlGetUtf16InternalEncoding)(void)
+{
+#if BYTEORDER == 1234
+  return &ns(internal_little2_encoding).enc;
+#elif BYTEORDER == 4321
+  return &ns(internal_big2_encoding).enc;
+#else
+  const short n = 1;
+  return (*(const char *)&n
+          ? &ns(internal_little2_encoding).enc
+          : &ns(internal_big2_encoding).enc);
+#endif
+}
+
+static const ENCODING * const NS(encodings)[] = {
+  &ns(latin1_encoding).enc,
+  &ns(ascii_encoding).enc,
+  &ns(utf8_encoding).enc,
+  &ns(big2_encoding).enc,
+  &ns(big2_encoding).enc,
+  &ns(little2_encoding).enc,
+  &ns(utf8_encoding).enc /* NO_ENC */
+};
+
+static int PTRCALL
+NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **nextTokPtr)
+{
+  return initScan(NS(encodings), (const INIT_ENCODING *)enc,
+                  XML_PROLOG_STATE, ptr, end, nextTokPtr);
+}
+
+static int PTRCALL
+NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end,
+                    const char **nextTokPtr)
+{
+  return initScan(NS(encodings), (const INIT_ENCODING *)enc,
+                  XML_CONTENT_STATE, ptr, end, nextTokPtr);
+}
+
+int
+NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr,
+                    const char *name)
+{
+  int i = getEncodingIndex(name);
+  if (i == UNKNOWN_ENC)
+    return 0;
+  SET_INIT_ENC_INDEX(p, i);
+  p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
+  p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
+  p->initEnc.updatePosition = initUpdatePosition;
+  p->encPtr = encPtr;
+  *encPtr = &(p->initEnc);
+  return 1;
+}
+
+static const ENCODING *
+NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end)
+{
+#define ENCODING_MAX 128
+  char buf[ENCODING_MAX];
+  char *p = buf;
+  int i;
+  XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
+  if (ptr != end)
+    return 0;
+  *p = 0;
+  if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2)
+    return enc;
+  i = getEncodingIndex(buf);
+  if (i == UNKNOWN_ENC)
+    return 0;
+  return NS(encodings)[i];
+}
+
+int
+NS(XmlParseXmlDecl)(int isGeneralTextEntity,
+                    const ENCODING *enc,
+                    const char *ptr,
+                    const char *end,
+                    const char **badPtr,
+                    const char **versionPtr,
+                    const char **versionEndPtr,
+                    const char **encodingName,
+                    const ENCODING **encoding,
+                    int *standalone)
+{
+  return doParseXmlDecl(NS(findEncoding),
+                        isGeneralTextEntity,
+                        enc,
+                        ptr,
+                        end,
+                        badPtr,
+                        versionPtr,
+                        versionEndPtr,
+                        encodingName,
+                        encoding,
+                        standalone);
+}
+
+#endif /* XML_TOK_NS_C */
diff --git a/deps/GLEW/GLEW.cmake b/deps/GLEW/GLEW.cmake
index fcbdcd20c..285b9be4d 100644
--- a/deps/GLEW/GLEW.cmake
+++ b/deps/GLEW/GLEW.cmake
@@ -1,5 +1,6 @@
 # We have to check for OpenGL to compile GLEW
-find_package(OpenGL QUIET REQUIRED)
+set(OpenGL_GL_PREFERENCE "LEGACY") # to prevent a nasty warning by cmake
+find_package(OpenGL QUIET)
 
 prusaslicer_add_cmake_project(
   GLEW
diff --git a/deps/OpenCSG/OpenCSG.cmake b/deps/OpenCSG/OpenCSG.cmake
index d9de5e152..fda74cd45 100644
--- a/deps/OpenCSG/OpenCSG.cmake
+++ b/deps/OpenCSG/OpenCSG.cmake
@@ -6,8 +6,8 @@ prusaslicer_add_cmake_project(OpenCSG
     DEPENDS dep_GLEW
 )
 
-if (TARGET dep_ZLIB)
-    add_dependencies(dep_OpenCSG dep_ZLIB)
+if (TARGET ${ZLIB_PKG})
+    add_dependencies(dep_OpenCSG ${ZLIB_PKG})
 endif()
 
 if (MSVC)
diff --git a/deps/PNG/PNG.cmake b/deps/PNG/PNG.cmake
new file mode 100644
index 000000000..01bd98499
--- /dev/null
+++ b/deps/PNG/PNG.cmake
@@ -0,0 +1,13 @@
+prusaslicer_add_cmake_project(PNG 
+    GIT_REPOSITORY https://github.com/glennrp/libpng.git 
+    GIT_TAG v1.6.35
+    DEPENDS ${ZLIB_PKG}
+    CMAKE_ARGS
+        -DPNG_SHARED=OFF
+        -DPNG_STATIC=ON
+        -DPNG_TESTS=OFF
+)
+
+if (MSVC)
+    add_debug_dep(dep_PNG)
+endif ()
diff --git a/deps/deps-linux.cmake b/deps/deps-linux.cmake
index 01f7ab21b..3ad3cca64 100644
--- a/deps/deps-linux.cmake
+++ b/deps/deps-linux.cmake
@@ -3,6 +3,13 @@ set(DEP_CMAKE_OPTS "-DCMAKE_POSITION_INDEPENDENT_CODE=ON")
 
 include("deps-unix-common.cmake")
 
+find_package(PNG QUIET)
+if (NOT PNG_FOUND)
+    message(WARNING "No PNG dev package found in system, building static library. You should install the system package.")
+endif ()
+
+#TODO UDEV
+
 ExternalProject_Add(dep_boost
     EXCLUDE_FROM_ALL 1
     URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz"
@@ -93,45 +100,4 @@ ExternalProject_Add(dep_libcurl
     INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
 )
 
-if (DEP_WX_STABLE)
-    set(DEP_WX_TAG "v3.0.4")
-else ()
-    set(DEP_WX_TAG "v3.1.1-patched")
-endif()
-
-if (DEP_WX_GTK3)
-    set(WX_GTK_VERSION "3")
-else ()
-    set(WX_GTK_VERSION "2")
-endif()
-
-ExternalProject_Add(dep_wxwidgets
-    EXCLUDE_FROM_ALL 1
-    GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
-    GIT_TAG "${DEP_WX_TAG}"
-    BUILD_IN_SOURCE 1
-    # PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h
-    CONFIGURE_COMMAND ./configure
-        "--prefix=${DESTDIR}/usr/local"
-        --disable-shared
-        --with-gtk=${WX_GTK_VERSION}
-        --with-opengl
-        --enable-unicode
-        --enable-graphics_ctx
-        --with-regex=builtin
-        --with-libpng=builtin
-        --with-libxpm=builtin
-        --with-libjpeg=builtin
-        --with-libtiff=builtin
-        --with-zlib
-        --with-expat=builtin
-        --disable-precomp-headers
-        --enable-debug_info
-        --enable-debug_gdb
-        --disable-debug
-        --disable-debug_flag
-    BUILD_COMMAND make "-j${NPROC}" && make -C locale allmo
-    INSTALL_COMMAND make install
-)
-
 add_dependencies(dep_openvdb dep_boost)
diff --git a/deps/deps-macos.cmake b/deps/deps-macos.cmake
index 17300b247..ef00b80d5 100644
--- a/deps/deps-macos.cmake
+++ b/deps/deps-macos.cmake
@@ -86,30 +86,4 @@ ExternalProject_Add(dep_libcurl
     INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
 )
 
-ExternalProject_Add(dep_wxwidgets
-    EXCLUDE_FROM_ALL 1
-    GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
-    GIT_TAG v3.1.3-patched
-    BUILD_IN_SOURCE 1
-#    PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h
-    CONFIGURE_COMMAND env "CXXFLAGS=${DEP_WERRORS_SDK}" "CFLAGS=${DEP_WERRORS_SDK}" ./configure
-        "--prefix=${DESTDIR}/usr/local"
-        --disable-shared
-        --with-osx_cocoa
-        --with-macosx-sdk=${CMAKE_OSX_SYSROOT}
-        "--with-macosx-version-min=${DEP_OSX_TARGET}"
-        --with-opengl
-        --with-regex=builtin
-        --with-libpng=builtin
-        --with-libxpm=builtin
-        --with-libjpeg=builtin
-        --with-libtiff=builtin
-        --with-zlib
-        --with-expat=builtin
-        --disable-debug
-        --disable-debug_flag
-    BUILD_COMMAND make "-j${NPROC}" && PATH=/usr/local/opt/gettext/bin/:$ENV{PATH} make -C locale allmo
-    INSTALL_COMMAND make install
-)
-
 add_dependencies(dep_openvdb dep_boost)
\ No newline at end of file
diff --git a/deps/deps-mingw.cmake b/deps/deps-mingw.cmake
index bfe5f9fe4..89b7e2b43 100644
--- a/deps/deps-mingw.cmake
+++ b/deps/deps-mingw.cmake
@@ -57,20 +57,4 @@ ExternalProject_Add(dep_libcurl
         -DCURL_DISABLE_GOPHER=ON
         -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
         ${DEP_CMAKE_OPTS}
-)
-
-ExternalProject_Add(dep_wxwidgets
-    EXCLUDE_FROM_ALL 1
-    GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
-    GIT_TAG v3.1.1-patched
-#    URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2"
-#    URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e
-#    PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}\\wxwidgets-pngprefix.h" src\\png\\pngprefix.h
-    CMAKE_ARGS
-        -DBUILD_SHARED_LIBS=OFF
-        -DwxUSE_LIBPNG=builtin
-        -DwxUSE_ZLIB=builtin
-        -DwxUSE_OPENGL=ON
-        -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
-        ${DEP_CMAKE_OPTS}
 )
\ No newline at end of file
diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake
index 3f3a70c6a..46c9f8864 100644
--- a/deps/deps-unix-common.cmake
+++ b/deps/deps-unix-common.cmake
@@ -9,9 +9,15 @@ endif ()
 
 find_package(ZLIB QUIET)
 if (NOT ZLIB_FOUND)
-    include(ZLIB/ZLIB.cmake)
+    message(WARNING "No ZLIB dev package found in system, building static library. You should install the system package.")
 endif ()
 
+# TODO Evaluate expat modifications in the bundled version and test with system versions in various distros and OSX SDKs
+# find_package(EXPAT QUIET)
+# if (NOT EXPAT_FOUND)
+#     message(WARNING "No EXPAT dev package found in system, building static library. Consider installing the system package.")
+# endif ()
+
 ExternalProject_Add(dep_tbb
     EXCLUDE_FROM_ALL 1
     URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz"
diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake
index 600168c1a..8be9888bc 100644
--- a/deps/deps-windows.cmake
+++ b/deps/deps-windows.cmake
@@ -149,38 +149,6 @@ ExternalProject_Add(dep_nlopt
 
 add_debug_dep(dep_nlopt)
 
-include(ZLIB/ZLIB.cmake)
-# ExternalProject_Add(dep_zlib
-#     EXCLUDE_FROM_ALL 1
-#     URL "https://zlib.net/zlib-1.2.11.tar.xz"
-#     URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066
-#     CMAKE_GENERATOR "${DEP_MSVC_GEN}"
-#     CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
-#     CMAKE_ARGS
-#         -DSKIP_INSTALL_FILES=ON                                    # Prevent installation of man pages et al.
-#         "-DINSTALL_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}\\fallout"   # I found no better way of preventing zlib from creating & installing DLLs :-/
-#         -DCMAKE_POSITION_INDEPENDENT_CODE=ON
-#         "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
-#     BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
-#     INSTALL_COMMAND ""
-# )
-
-add_debug_dep(dep_ZLIB)
-
-# The following steps are unfortunately needed to remove the _static suffix on libraries
-# ExternalProject_Add_Step(dep_zlib fix_static
-#     DEPENDEES install
-#     COMMAND "${CMAKE_COMMAND}" -E rename zlibstatic.lib zlib.lib
-#     WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\"
-# )
-# if (${DEP_DEBUG})
-#     ExternalProject_Add_Step(dep_zlib fix_static_debug
-#         DEPENDEES install
-#         COMMAND "${CMAKE_COMMAND}" -E rename zlibstaticd.lib zlibd.lib
-#         WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\"
-#     )
-# endif ()
-
 if (${DEPS_BITS} EQUAL 32)
     set(DEP_LIBCURL_TARGET "x86")
 else ()
@@ -243,36 +211,13 @@ endif ()
 
 find_package(Git REQUIRED)
 
-ExternalProject_Add(dep_wxwidgets
-    EXCLUDE_FROM_ALL 1
-    GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
-    GIT_TAG v3.1.1-patched
-#    URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2"
-#    URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e
-    BUILD_IN_SOURCE 1
-#    PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}\\wxwidgets-pngprefix.h" src\\png\\pngprefix.h
-    CONFIGURE_COMMAND ""
-    BUILD_COMMAND cd build\\msw && nmake /f makefile.vc BUILD=release SHARED=0 UNICODE=1 USE_GUI=1 "${DEP_WXWIDGETS_TARGET}"
-    INSTALL_COMMAND "${CMAKE_COMMAND}" -E copy_directory include "${DESTDIR}\\usr\\local\\include"
-        && "${CMAKE_COMMAND}" -E copy_directory "lib\\${DEP_WXWIDGETS_LIBDIR}" "${DESTDIR}\\usr\\local\\lib\\${DEP_WXWIDGETS_LIBDIR}"
-)
-if (${DEP_DEBUG})
-    ExternalProject_Get_Property(dep_wxwidgets SOURCE_DIR)
-    ExternalProject_Add_Step(dep_wxwidgets build_debug
-        DEPENDEES build
-        DEPENDERS install
-        COMMAND cd build\\msw && nmake /f makefile.vc BUILD=debug SHARED=0 UNICODE=1 USE_GUI=1 "${DEP_WXWIDGETS_TARGET}"
-        WORKING_DIRECTORY "${SOURCE_DIR}"
-    )
-endif ()
-
 ExternalProject_Add(dep_blosc
     EXCLUDE_FROM_ALL 1
     #URL https://github.com/Blosc/c-blosc/archive/v1.17.0.zip
     #URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9
     GIT_REPOSITORY https://github.com/Blosc/c-blosc.git
     GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0
-    DEPENDS dep_ZLIB
+    DEPENDS ${ZLIB_PKG}
     CMAKE_GENERATOR "${DEP_MSVC_GEN}"
     CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
     CMAKE_ARGS
@@ -299,7 +244,7 @@ ExternalProject_Add(dep_openexr
     EXCLUDE_FROM_ALL 1
     GIT_REPOSITORY https://github.com/openexr/openexr.git
     GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 
-    DEPENDS dep_ZLIB
+    DEPENDS ${ZLIB_PKG}
     CMAKE_GENERATOR "${DEP_MSVC_GEN}"
     CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
     CMAKE_ARGS
@@ -352,4 +297,4 @@ if (${DEP_DEBUG})
         COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
         WORKING_DIRECTORY "${BINARY_DIR}"
     )
-endif ()
+endif ()
\ No newline at end of file
diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake
new file mode 100644
index 000000000..4f89fe82c
--- /dev/null
+++ b/deps/wxWidgets/wxWidgets.cmake
@@ -0,0 +1,36 @@
+set(_wx_git_tag v3.1.3-patched)
+
+# set(_patch_command "")
+set(_wx_toolkit "")
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    set(_gtk_ver 2)
+    if (DEP_WX_GTK3)
+        set(_gtk_ver 3)
+    endif ()
+    set(_wx_toolkit "-DwxBUILD_TOOLKIT=gtk${_gtk_ver}")
+endif()
+
+prusaslicer_add_cmake_project(wxWidgets
+    GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
+    GIT_TAG ${_wx_git_tag}
+    # PATCH_COMMAND "${_patch_command}"
+    DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG}
+    CMAKE_ARGS
+        -DwxBUILD_PRECOMP=ON
+        ${_wx_toolkit}
+        "-DCMAKE_DEBUG_POSTFIX:STRING="
+        -DwxUSE_DETECT_SM=OFF
+        -DwxUSE_UNICODE=ON
+        -DwxUSE_OPENGL=ON
+        -DwxUSE_LIBPNG=sys
+        -DwxUSE_ZLIB=sys
+        -DwxUSE_REGEX=builtin
+        -DwxUSE_LIBXPM=builtin
+        -DwxUSE_LIBJPEG=builtin
+        -DwxUSE_LIBTIFF=builtin
+        -DwxUSE_EXPAT=sys
+)
+
+if (MSVC)
+    add_debug_dep(dep_wxWidgets)
+endif ()
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b0eab9bcc..cdc8e0a13 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -59,6 +59,10 @@ if (SLIC3R_GUI)
 
     include(${wxWidgets_USE_FILE})
 
+    list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX png)
+    list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat)
+    list(APPEND wxWidgets_LIBRARIES ${PNG_LIBRARIES} ${EXPAT_LIBRARIES})
+
 #    list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
     message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
 
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index eb0f87e50..30614100f 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -272,7 +272,8 @@ endif ()
 encoding_check(libslic3r)
 
 target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0)
-target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
+target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
+target_include_directories(libslic3r PUBLIC ${EXPAT_INCLUDE_DIRS})
 target_link_libraries(libslic3r
     libnest2d
     admesh
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 2ac8a1ff0..3711b12a1 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -204,7 +204,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
 
 encoding_check(libslic3r_gui)
 
-target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES})
+target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES} rt)
 
 if(APPLE)
     target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})

From ec81de75532fe45f880eb95ad2393cca1555101b Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 28 Apr 2020 17:19:11 +0200
Subject: [PATCH 44/55] Ironing and Monotonous infill - first working
 implementation.

---
 src/libslic3r/Fill/Fill.cpp             |   3 +-
 src/libslic3r/Fill/FillRectilinear2.cpp | 684 ++++++++++++------------
 src/libslic3r/PrintConfig.cpp           |   2 +-
 src/libslic3r/ShortestPath.cpp          |   5 +-
 4 files changed, 342 insertions(+), 352 deletions(-)

diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index ad1830e5f..3c16527f0 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -532,7 +532,8 @@ void Layer::make_ironing()
     fill.z 					 = this->print_z;
     fill.overlap 			 = 0;
     fill_params.density 	 = 1.;
-    fill_params.dont_connect = true;
+//    fill_params.dont_connect = true;
+    fill_params.dont_connect = false;
     fill_params.monotonous   = true;
 
 	for (size_t i = 0; i < by_extruder.size(); ++ i) {
diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp
index 6dd6bb883..e16d57ad6 100644
--- a/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -160,19 +160,7 @@ struct SegmentIntersection
     enum class LinkQuality : uint8_t {
     	Invalid,
         Valid,
-    	// Valid link, to be followed when extruding.
-    	// Link inside a monotonous region.
-    	ValidMonotonous,
-    	// Valid link, to be possibly followed when extruding.
-    	// Link between two monotonous regions.
-    	ValidNonMonotonous,
-    	// Link from T to end of another contour.
-    	FromT,
-    	// Link from end of one contour to T.
-    	ToT,
-    	// Link from one T to another T, making a letter H.
-    	H,
-    	// Vertical segment
+    	// Valid link, but too long to be followed.
     	TooLong,
     };
 
@@ -231,6 +219,10 @@ struct SegmentIntersection
     int 	left_horizontal()  					const { return this->has_left_horizontal() 	? this->prev_on_contour : -1; }
     int 	right_horizontal()  				const { return this->has_right_horizontal() ? this->next_on_contour : -1; }
     int 	horizontal(Side side)  				const { return side == Side::Left ? this->left_horizontal() : this->right_horizontal(); }
+    LinkQuality horizontal_quality(Side side)	const {
+    	assert(this->has_horizontal(side));
+    	return side == Side::Left ? this->prev_on_contour_quality : this->next_on_contour_quality;
+    }
 
     int 	left_vertical_up()   		 		const { return this->has_left_vertical_up()    ? this->prev_on_contour : -1; }
     int 	left_vertical_down()   		 		const { return this->has_left_vertical_down()  ? this->prev_on_contour : -1; }
@@ -251,7 +243,6 @@ struct SegmentIntersection
     	return this->has_left_vertical_up() ? this->left_vertical_up() : this->right_vertical_up();
     }
     LinkQuality vertical_up_quality()			const {
-    	assert(this->has_left_vertical_up() != this->has_right_vertical_up());
     	return this->has_left_vertical_up() ? this->prev_on_contour_quality : this->next_on_contour_quality;
     }
     // Returns -1 if there is no link down.
@@ -260,10 +251,10 @@ struct SegmentIntersection
     	return this->has_left_vertical_down() ? this->left_vertical_down() : this->right_vertical_down();
     }
     LinkQuality vertical_down_quality()			const {
-    	assert(this->has_left_vertical_down() != this->has_right_vertical_down());
     	return this->has_left_vertical_down() ? this->prev_on_contour_quality : this->next_on_contour_quality;
     }
     int 	vertical_outside()					const { return this->is_low() ? this->vertical_down() : this->vertical_up(); }
+    LinkQuality vertical_outside_quality()		const { return this->is_low() ? this->vertical_down_quality() : this->vertical_up_quality(); }
 
     // Compare two y intersection points given by rational numbers.
     // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32.
@@ -463,25 +454,8 @@ static inline int distance_of_segmens(const Polygon &poly, size_t seg1, size_t s
     return d;
 }
 
-enum IntersectionTypeOtherVLine {
-    // There is no connection point on the other vertical line.
-    INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED = -1,
-    // Connection point on the other vertical segment was found
-    // and it could be followed.
-    INTERSECTION_TYPE_OTHER_VLINE_OK = 0,
-    // The connection segment connects to a middle of a vertical segment.
-    // Cannot follow.
-    INTERSECTION_TYPE_OTHER_VLINE_INNER,
-    // Cannot extend the contor to this intersection point as either the connection segment
-    // or the succeeding vertical segment were already consumed.
-    INTERSECTION_TYPE_OTHER_VLINE_CONSUMED,
-    // Not the first intersection along the contor. This intersection point
-    // has been preceded by an intersection point along the vertical line.
-    INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST,
-};
-
 // Find an intersection on a previous line, but return -1, if the connecting segment of a perimeter was already extruded.
-static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical_line(
+static inline bool intersection_on_prev_next_vertical_line_valid(
     const std::vector<SegmentedIntersectionLine>  &segs,
     size_t                                         iVerticalLine,
     size_t                                         iIntersection,
@@ -492,10 +466,10 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
 	if (it_this.has_vertical(side))
 	    // Not the first intersection along the contor. This intersection point
 	    // has been preceded by an intersection point along the vertical line.
-		return INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST;
+		return false;
     int iIntersectionOther = it_this.horizontal(side);
     if (iIntersectionOther == -1)
-        return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
+        return false;
     assert(side == SegmentIntersection::Side::Right ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
     const SegmentedIntersectionLine &vline_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine + 1) : (iVerticalLine - 1)];
     const SegmentIntersection       &it_other    = vline_other.intersections[iIntersectionOther];
@@ -507,52 +481,50 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
     if (it_other2.is_inner())
         // Cannot follow a perimeter segment into the middle of another vertical segment.
         // Only perimeter segments connecting to the end of a vertical segment are followed.
-        return INTERSECTION_TYPE_OTHER_VLINE_INNER;
+        return false;
     assert(it_other.is_low() == it_other2.is_low());
+    if (it_this.horizontal_quality(side) != SegmentIntersection::LinkQuality::Valid)
+    	return false;
     if (side == SegmentIntersection::Side::Right ? it_this.consumed_perimeter_right : it_other.consumed_perimeter_right)
         // This perimeter segment was already consumed.
-        return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED;
+        return false;
     if (it_other.is_low() ? it_other.consumed_vertical_up : vline_other.intersections[iIntersectionOther - 1].consumed_vertical_up)
         // This vertical segment was already consumed.
-        return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED;
-    return INTERSECTION_TYPE_OTHER_VLINE_OK;
+        return false;
+#if 0
+    if (it_other.vertical_outside() != -1 && it_other.vertical_outside_quality() == SegmentIntersection::LinkQuality::Valid)
+        // Landed inside a vertical run. Stop here.
+        return false;
+#endif
+    return true;
 }
 
-static inline IntersectionTypeOtherVLine intersection_type_on_prev_vertical_line(
+static inline bool intersection_on_prev_vertical_line_valid(
     const std::vector<SegmentedIntersectionLine>  &segs, 
     size_t                                         iVerticalLine, 
     size_t                                         iIntersection)
 {
-    return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Left);
+    return intersection_on_prev_next_vertical_line_valid(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Left);
 }
 
-static inline IntersectionTypeOtherVLine intersection_type_on_next_vertical_line(
+static inline bool intersection_on_next_vertical_line_valid(
     const std::vector<SegmentedIntersectionLine>  &segs, 
     size_t                                         iVerticalLine, 
     size_t                                         iIntersection)
 {
-    return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Right);
+    return intersection_on_prev_next_vertical_line_valid(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Right);
 }
 
 // Measure an Euclidian length of a perimeter segment when going from iIntersection to iIntersection2.
-static inline coordf_t measure_perimeter_prev_next_segment_length(
+static inline coordf_t measure_perimeter_horizontal_segment_length(
     const ExPolygonWithOffset                     &poly_with_offset, 
     const std::vector<SegmentedIntersectionLine>  &segs,
     size_t                                         iVerticalLine,
     size_t                                         iIntersection,
-    size_t                                         iIntersection2,
-    bool                                           dir_is_next)
+    size_t                                         iIntersection2)
 {
-    size_t iVerticalLineOther = iVerticalLine;
-    if (dir_is_next) {
-        if (++ iVerticalLineOther == segs.size())
-            // No successive vertical line.
-            return coordf_t(-1);
-    } else if (iVerticalLineOther -- == 0) {
-        // No preceding vertical line.
-        return coordf_t(-1);
-    }
-
+    size_t                           iVerticalLineOther = iVerticalLine + 1;
+    assert(iVerticalLineOther < segs.size());
     const SegmentedIntersectionLine &vline  = segs[iVerticalLine];
     const SegmentIntersection       &it     = vline.intersections[iIntersection];
     const SegmentedIntersectionLine &vline2 = segs[iVerticalLineOther];
@@ -562,36 +534,14 @@ static inline coordf_t measure_perimeter_prev_next_segment_length(
 //    const bool                       ccw    = poly_with_offset.is_contour_ccw(vline.iContour);
     assert(it.type == it2.type);
     assert(it.iContour == it2.iContour);
-    assert(it.is_inner());
-    const bool                       forward = it.is_low() == dir_is_next;
 
     Point p1(vline.pos,  it.pos());
     Point p2(vline2.pos, it2.pos());
-    return forward ?
+    return it.is_low() ?
         segment_length(poly, it .iSegment, p1, it2.iSegment, p2) :
         segment_length(poly, it2.iSegment, p2, it .iSegment, p1);
 }
 
-static inline coordf_t measure_perimeter_prev_segment_length(
-    const ExPolygonWithOffset                     &poly_with_offset,
-    const std::vector<SegmentedIntersectionLine>  &segs,
-    size_t                                         iVerticalLine,
-    size_t                                         iIntersection,
-    size_t                                         iIntersection2)
-{
-    return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, false);
-}
-
-static inline coordf_t measure_perimeter_next_segment_length(
-    const ExPolygonWithOffset                     &poly_with_offset,
-    const std::vector<SegmentedIntersectionLine>  &segs,
-    size_t                                         iVerticalLine,
-    size_t                                         iIntersection,
-    size_t                                         iIntersection2)
-{
-    return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, true);
-}
-
 // Append the points of a perimeter segment when going from iIntersection to iIntersection2.
 // The first point (the point of iIntersection) will not be inserted,
 // the last point will be inserted.
@@ -646,8 +596,7 @@ static inline coordf_t measure_perimeter_segment_on_vertical_line_length(
     const SegmentIntersection       &itsct = il.intersections[iIntersection];
     const SegmentIntersection       &itsct2 = il.intersections[iIntersection2];
     const Polygon                   &poly = poly_with_offset.contour(itsct.iContour);
-    assert(itsct.is_inner());
-    assert(itsct2.is_inner());
+    assert(itsct.is_inner() == itsct2.is_inner());
     assert(itsct.type != itsct2.type);
     assert(itsct.iContour == itsct2.iContour);
     Point p1(il.pos, itsct.pos());
@@ -943,7 +892,9 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
 // Connect each contour / vertical line intersection point with another two contour / vertical line intersection points.
 // (fill in SegmentIntersection::{prev_on_contour, prev_on_contour_vertical, next_on_contour, next_on_contour_vertical}.
 // These contour points are either on the same vertical line, or on the vertical line left / right to the current one.
-static void connect_segment_intersections_by_contours(const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs)
+static void connect_segment_intersections_by_contours(
+	const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs,
+	const FillParams &params, const coord_t link_max_length)
 {
     for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
 	    SegmentedIntersectionLine       &il      = segs[i_vline];
@@ -1026,13 +977,88 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
             itsct.next_on_contour_type  = same_next ?
                 (inext < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) :
                 SegmentIntersection::LinkType::Horizontal;
+
+        	if (same_prev) {
+        		// Only follow a vertical perimeter segment if it skips just the outer intersections.
+        		SegmentIntersection *it  = &itsct;
+        		SegmentIntersection *end = il.intersections.data() + iprev;
+        		assert(it != end);
+        		if (it > end)
+        			std::swap(it, end);
+                for (++ it; it != end; ++ it)
+                    if (it->is_inner()) {
+        				itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
+                        break;
+                    }
+            }
+
+        	if (same_next) {
+        		// Only follow a vertical perimeter segment if it skips just the outer intersections.
+        		SegmentIntersection *it  = &itsct;
+        		SegmentIntersection *end = il.intersections.data() + inext;
+        		assert(it != end);
+        		if (it > end)
+        			std::swap(it, end);
+                for (++ it; it != end; ++ it)
+                    if (it->is_inner()) {
+        				itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
+                        break;
+                    }
+            }
+
+            // If both iprev and inext are on this vline, then there must not be any intersection with the previous or next contour and we will
+            // not trace this contour when generating infill.
+            if (same_prev && same_next) {
+            	assert(iprev != i_intersection);
+            	assert(inext != i_intersection);
+            	if ((iprev > i_intersection) == (inext > i_intersection)) {
+            		// Both closest intersections of this contour are on the same vertical line and at the same side of this point.
+            		// Ignore them when tracing the infill.
+	                itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
+	                itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
+	            }
+            }
+
+			if (params.dont_connect) {
+				if (itsct.prev_on_contour_quality == SegmentIntersection::LinkQuality::Valid)
+					itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::TooLong;
+				if (itsct.next_on_contour_quality == SegmentIntersection::LinkQuality::Valid)
+					itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::TooLong;
+			} else if (link_max_length > 0) {
+            	// Measure length of the links.
+				if (itsct.prev_on_contour_quality == SegmentIntersection::LinkQuality::Valid &&
+            	    (same_prev ? 
+            		 	measure_perimeter_segment_on_vertical_line_length(poly_with_offset, segs, i_vline, iprev, i_intersection, forward) :
+            			measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline - 1, iprev, i_intersection)) > link_max_length)
+	    			itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::TooLong;
+				if (itsct.next_on_contour_quality == SegmentIntersection::LinkQuality::Valid &&
+            		(same_next ?
+            			measure_perimeter_segment_on_vertical_line_length(poly_with_offset, segs, i_vline, i_intersection, inext, forward) :
+            			measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline, i_intersection, inext)) > link_max_length)
+	    			itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::TooLong;
+            }
 	    }
+
+	    // Make the LinkQuality::Invalid symmetric on vertical connections.
+        for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) {
+		    SegmentIntersection &it = il.intersections[i_intersection];
+            if (it.has_left_vertical() && it.prev_on_contour_quality == SegmentIntersection::LinkQuality::Invalid) {
+			    SegmentIntersection &it2 = il.intersections[it.left_vertical()];
+			    assert(it2.left_vertical() == i_intersection);
+			    it2.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
+            }
+            if (it.has_right_vertical() && it.next_on_contour_quality == SegmentIntersection::LinkQuality::Invalid) {
+			    SegmentIntersection &it2 = il.intersections[it.right_vertical()];
+			    assert(it2.right_vertical() == i_intersection);
+			    it2.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
+            }
+		}
     }
 
 #ifndef NDEBUG
     // Validate the connectivity.
     for (size_t i_vline = 0; i_vline + 1 < segs.size(); ++ i_vline) {
-        const SegmentedIntersectionLine &il_left    = segs[i_vline];
+        const SegmentedIntersectionLine &il_left  = segs[i_vline];
         const SegmentedIntersectionLine &il_right = segs[i_vline + 1];
         for (const SegmentIntersection &it : il_left.intersections) {
             if (it.has_right_horizontal()) {
@@ -1055,6 +1081,28 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
             }
         }
     }
+    for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
+        const SegmentedIntersectionLine &il = segs[i_vline];
+        for (const SegmentIntersection &it : il.intersections) {
+            auto i_it = int(&it - il.intersections.data());
+            if (it.has_left_vertical_up()) {
+                assert(il.intersections[it.left_vertical_up()].left_vertical_down() == i_it);
+                assert(il.intersections[it.left_vertical_up()].prev_on_contour_quality == it.prev_on_contour_quality);
+            }
+            if (it.has_left_vertical_down()) {
+                assert(il.intersections[it.left_vertical_down()].left_vertical_up() == i_it);
+                assert(il.intersections[it.left_vertical_down()].prev_on_contour_quality == it.prev_on_contour_quality);
+            }
+            if (it.has_right_vertical_up()) {
+                assert(il.intersections[it.right_vertical_up()].right_vertical_down() == i_it);
+                assert(il.intersections[it.right_vertical_up()].next_on_contour_quality == it.next_on_contour_quality);
+            }
+            if (it.has_right_vertical_down()) {
+                assert(il.intersections[it.right_vertical_down()].right_vertical_up() == i_it);
+                assert(il.intersections[it.right_vertical_down()].next_on_contour_quality == it.next_on_contour_quality);
+            }
+        }
+    }
 #endif /* NDEBUG */
 }
 
@@ -1104,161 +1152,6 @@ static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, S
 	return const_cast<SegmentIntersection&>(end_of_vertical_run(std::as_const(il), std::as_const(start)));
 }
 
-static void classify_vertical_runs(
-	const ExPolygonWithOffset &poly_with_offset, const FillParams &params, const coord_t link_max_length, 
-	std::vector<SegmentedIntersectionLine> &segs, size_t i_vline)
-{
-	SegmentedIntersectionLine &vline = segs[i_vline];
-    for (size_t i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) {
-    	if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) {
-    		if (vline.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) {
-    			for (;;) {
-        			SegmentIntersection &start = vline.intersections[i_intersection];
-			        SegmentIntersection &end   = end_of_vertical_run_raw(start);
-			        SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid;
-					// End of a contour starting at end and ending above end at the same vertical line.
-					int inext = end.vertical_outside();
-					if (inext == -1) {
-						i_intersection = &end - vline.intersections.data() + 1;
-						break;
-					}
-        			SegmentIntersection &start2 = vline.intersections[inext];
-        			if (params.dont_connect)
-        				link_quality = SegmentIntersection::LinkQuality::TooLong;
-        			else {
-                        for (SegmentIntersection *it = &end + 1; it != &start2; ++ it)
-                            if (it->is_inner()) {
-		        				link_quality = SegmentIntersection::LinkQuality::Invalid;
-                                break;
-                            }
-        				if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) {
-                        	// Measure length of the link.
-	                        coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
-	                            poly_with_offset, segs, i_vline, i_intersection, inext, end.has_right_vertical_outside());
-	                        if (link_length > link_max_length)
-		        				link_quality = SegmentIntersection::LinkQuality::TooLong;
-                        }
-                    }
-                    (end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality;
-                    (start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality;
-                    if (link_quality != SegmentIntersection::LinkQuality::Valid) {
-						i_intersection = &end - vline.intersections.data() + 1;
-                    	break;
-                    }
-					i_intersection = &start2 - vline.intersections.data();
-                }
-    		} else
-    			++ i_intersection;
-    	} else
-    		++ i_intersection;
-    }	
-}
-
-static void classify_horizontal_links(
-	const ExPolygonWithOffset &poly_with_offset, const FillParams &params, const coord_t link_max_length, 
-	std::vector<SegmentedIntersectionLine> &segs, size_t i_vline)
-{
-	SegmentedIntersectionLine &vline_left  = segs[i_vline];
-	SegmentedIntersectionLine &vline_right = segs[i_vline + 1];
-
-	// Traverse both left and right together.
-	size_t i_intersection_left  = 0;
-	size_t i_intersection_right = 0;
-	while (i_intersection_left + 1 < vline_left.intersections.size() && i_intersection_right + 1 < vline_right.intersections.size()) {
-    	if (i_intersection_left < vline_left.intersections.size() && vline_left.intersections[i_intersection_left].type != SegmentIntersection::INNER_LOW) {
-    		++ i_intersection_left;
-    		continue;
-    	}
-    	if (i_intersection_right < vline_right.intersections.size() && vline_right.intersections[i_intersection_right].type != SegmentIntersection::INNER_LOW) {
-    		++ i_intersection_right;
-    		continue;
-    	}
-
-		if (i_intersection_left + 1 >= vline_left.intersections.size()) {
-			// Trace right only.
-		} else if (i_intersection_right + 1 >= vline_right.intersections.size()) {
-			// Trace left only.
-		} else {
-			// Trace both.
-			SegmentIntersection &start_left  = vline_left.intersections[i_intersection_left];
-	        SegmentIntersection &end_left    = end_of_vertical_run(vline_left, start_left);
-			SegmentIntersection &start_right = vline_right.intersections[i_intersection_right];
-	        SegmentIntersection &end_right   = end_of_vertical_run(vline_right, start_right);
-	        // Do these runs overlap?
-	        int                    end_right_horizontal = end_left.right_horizontal();
-	        int                    end_left_horizontal  = end_right.left_horizontal();
-	        if (end_right_horizontal != -1) {
-	        	if (end_right_horizontal < &start_right - vline_right.intersections.data()) {
-	        		// Left precedes the right segment.
-	        	}
-	        } else if (end_left_horizontal != -1) {
-	        	if (end_left_horizontal < &start_left - vline_left.intersections.data()) {
-	        		// Right precedes the left segment.
-	        	}
-	        }
-		}
-	}
-
-#if 0
-    for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) {
-    	if (segs.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) {
-    		if (segs.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) {
-    			for (;;) {
-        			SegmentIntersection &start = segs.intersections[i_intersection];
-			        SegmentIntersection &end   = end_of_vertical_run_raw(start);
-			        SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid;
-					// End of a contour starting at end and ending above end at the same vertical line.
-					int inext = end.vertical_outside();
-					if (inext == -1) {
-						i_intersection = &end - segs.intersections.data() + 1;
-						break;
-					}
-        			SegmentIntersection &start2 = segs.intersections[inext];
-        			if (params.dont_connect)
-        				link_quality = SegmentIntersection::LinkQuality::TooLong;
-        			else {
-                        for (SegmentIntersection *it = &end + 1; it != &start2; ++ it)
-                            if (it->is_inner()) {
-		        				link_quality = SegmentIntersection::LinkQuality::Invalid;
-                                break;
-                            }
-        				if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) {
-                        	// Measure length of the link.
-	                        coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
-	                            poly_with_offset, segs, i_vline, i_intersection, inext, intrsctn->has_right_vertical_outside());
-	                        if (link_length > link_max_length)
-		        				link_quality = SegmentIntersection::LinkQuality::TooLong;
-                        }
-                    }
-                    (end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality;
-                    (start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality;
-                    if (link_quality != SegmentIntersection::LinkQuality::Valid) {
-						i_intersection = &end - segs.intersections.data() + 1;
-                    	break;
-                    }
-					i_intersection = &start2 - segs.intersections.data();
-                }
-    		} else
-    			++ i_intersection;
-    	} else
-    		++ i_intersection;
-    }
-#endif
-}
-
-static void disconnect_invalid_contour_links(
-	const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs)
-{
-	// Make the links symmetric!
-
-	// Validate vertical runs including vertical contour links.
-    for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
-		classify_vertical_runs(poly_with_offset, params, link_max_length, segs, i_vline);
-		if (i_vline > 0)
-			classify_horizontal_links(poly_with_offset, params, link_max_length, segs, i_vline - 1);
-    }
-}
-
 static void traverse_graph_generate_polylines(
 	const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs, Polylines& polylines_out)
 {
@@ -1392,44 +1285,30 @@ static void traverse_graph_generate_polylines(
         if (try_connect) {
             // Decide, whether to finish the segment, or whether to follow the perimeter.
             // 1) Find possible connection points on the previous / next vertical line.
-            IntersectionTypeOtherVLine intrsection_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection);
-            IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection);
+        	int  i_prev = it->left_horizontal();
+        	int  i_next = it->right_horizontal();
+            bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection);
+            bool intersection_next_valid = intersection_on_next_vertical_line_valid(segs, i_vline, i_intersection);
+            bool intersection_horizontal_valid = intersection_prev_valid || intersection_next_valid;
+            // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
+            if (i_prev != -1)
+                segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
+            if (i_next != -1)
+                it->consumed_perimeter_right = true;
+
             // Try to connect to a previous or next vertical line, making a zig-zag pattern.
-            if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) {
+            if (intersection_horizontal_valid) {
             	// A horizontal connection along the perimeter line exists.
-            	int i_prev = it->left_horizontal();
-            	int i_next = it->right_horizontal();
-                coordf_t dist_prev = (intrsection_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
-                    measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_prev);
-                coordf_t dist_next = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
-                    measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next);
-                // Take the shorter path.
-                //FIXME this may not be always the best strategy to take the shortest connection line now.
-                bool take_next = (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ?
-                    (dist_next < dist_prev) :
-                    intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK;
-                assert(it->is_inner());
-                bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? dist_next : dist_prev) > link_max_length);
-                if (skip) {
-#if 1
-                    // Just skip the connecting contour and start a new path.
-                    goto dont_connect;
-#else                    
-                    polyline_current->points.emplace_back(vline.pos, it->pos());
-                    polylines_out.emplace_back();
-                    polyline_current = &polylines_out.back();
-                    const SegmentedIntersectionLine& il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)];
-                    polyline_current->points.emplace_back(il2.pos, il2.intersections[take_next ? i_next : i_prev].pos());
-#endif
-                } else {
-                    polyline_current->points.emplace_back(vline.pos, it->pos());
-                    emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next);
-                }
-                // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
-                if (i_prev != -1)
-                    segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
-                if (i_next != -1)
-                    it->consumed_perimeter_right = true;
+	            assert(it->is_inner());
+            	bool take_next = intersection_next_valid;
+            	if (intersection_prev_valid && intersection_next_valid) {
+            		// Take the shorter segment. This greedy heuristics may not be the best.
+            		coordf_t dist_prev = measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline - 1, i_prev, i_intersection);
+	                coordf_t dist_next = measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next);
+	                take_next = dist_next < dist_prev;
+	            }
+                polyline_current->points.emplace_back(vline.pos, it->pos());
+                emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next);
                 //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed.
                 // Advance to the neighbor line.
                 if (take_next) {
@@ -1443,64 +1322,43 @@ static void traverse_graph_generate_polylines(
                 continue;
             }
 
-            // 5) Try to connect to a previous or next point on the same vertical line.
-            if (int inext = it->vertical_outside(); inext != -1) {
-                bool valid = true;
-                // Verify, that there is no intersection with the inner contour up to the end of the contour segment.
-                // Verify, that the successive segment has not been consumed yet.
-                if (going_up) {
-                    if (vline.intersections[inext].consumed_vertical_up)
-                        valid = false;
-                    else {
-                        for (int i = i_intersection + 1; i < inext && valid; ++ i)
-                            if (vline.intersections[i].is_inner())
-                                valid = false;
-                    }
-                } else {
-                    if (vline.intersections[inext - 1].consumed_vertical_up)
-                        valid = false;
-                    else {
-                        for (int i = inext + 1; i < i_intersection && valid; ++ i)
-                            if (vline.intersections[i].is_inner())
-                                valid = false;
-                    }
-                }
-                if (valid) {
-                    const Polygon &poly = poly_with_offset.contour(it->iContour);
-                    assert(it->iContour == vline.intersections[inext].iContour);
-                    // Skip this perimeter line?
-                    bool skip = params.dont_connect;
-                    bool dir_forward = it->has_right_vertical_outside();
-                    if (! skip && link_max_length > 0) {
-                        coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
-                            poly_with_offset, segs, i_vline, i_intersection, inext, dir_forward);
-                        skip = link_length > link_max_length;
-                    }
-                    polyline_current->points.emplace_back(vline.pos, it->pos());
-                    if (skip) {
-                        // Just skip the connecting contour and start a new path.
-                        polylines_out.emplace_back();
-                        polyline_current = &polylines_out.back();
-                        polyline_current->points.emplace_back(vline.pos, vline.intersections[inext].pos());
-                    } else {
-                        // Consume the connecting contour and the next segment.
-                        emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, inext, *polyline_current, dir_forward);
-                    }
-                    // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
-                    // If there are any outer intersection points skipped (bypassed) by the contour,
-                    // mark them as processed.
-                    if (going_up)
-                        for (int i = i_intersection; i < inext; ++ i)
-                            vline.intersections[i].consumed_vertical_up = true;
-                    else
-                        for (int i = inext; i < i_intersection; ++ i)
-                            vline.intersections[i].consumed_vertical_up = true;
-                    // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true;
-                    it->consumed_perimeter_right = true;
-                    (going_up ? ++ it : -- it)->consumed_perimeter_right = true;
-                    i_intersection = inext;
-                    continue;
+            // Try to connect to a previous or next point on the same vertical line.
+            int i_vertical = it->vertical_outside();
+            auto vertical_link_quality = (i_vertical == -1 || vline.intersections[i_vertical + (going_up ? 0 : -1)].consumed_vertical_up) ? 
+            	SegmentIntersection::LinkQuality::Invalid : it->vertical_outside_quality();
+#if 0            	
+            if (vertical_link_quality == SegmentIntersection::LinkQuality::Valid ||
+            	// Follow the link if there is no horizontal link available.
+            	(! intersection_horizontal_valid && vertical_link_quality != SegmentIntersection::LinkQuality::Invalid)) {
+#else
+           	if (vertical_link_quality != SegmentIntersection::LinkQuality::Invalid) {
+#endif
+                assert(it->iContour == vline.intersections[i_vertical].iContour);
+                polyline_current->points.emplace_back(vline.pos, it->pos());
+                if (vertical_link_quality == SegmentIntersection::LinkQuality::Valid)
+                    // Consume the connecting contour and the next segment.
+                    emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, i_vertical,
+                        *polyline_current, going_up ? it->has_left_vertical_up() : it->has_right_vertical_down());
+                else {
+                    // Just skip the connecting contour and start a new path.
+                    polylines_out.emplace_back();
+                    polyline_current = &polylines_out.back();
+                    polyline_current->points.emplace_back(vline.pos, vline.intersections[i_vertical].pos());
                 }
+                // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
+                // If there are any outer intersection points skipped (bypassed) by the contour,
+                // mark them as processed.
+                if (going_up)
+                    for (int i = i_intersection; i < i_vertical; ++i)
+                        vline.intersections[i].consumed_vertical_up = true;
+                else
+                    for (int i = i_vertical; i < i_intersection; ++i)
+                        vline.intersections[i].consumed_vertical_up = true;
+                // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true;
+                it->consumed_perimeter_right = true;
+                (going_up ? ++it : --it)->consumed_perimeter_right = true;
+                i_intersection = i_vertical;
+                continue;
             }
 
         dont_connect:
@@ -1553,10 +1411,16 @@ struct MonotonousRegion
     int 		left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; }
     int 		right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; }
 
+#if NDEBUG
     // Left regions are used to track whether all regions left to this one have already been printed.
     boost::container::small_vector<MonotonousRegion*, 4>	left_neighbors;
     // Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics.
     boost::container::small_vector<MonotonousRegion*, 4>	right_neighbors;
+#else
+    // For debugging, use the normal vector as it is better supported by debug visualizers.
+    std::vector<MonotonousRegion*> left_neighbors;
+    std::vector<MonotonousRegion*> right_neighbors;
+#endif
 };
 
 struct AntPath
@@ -1607,7 +1471,7 @@ public:
 				int i_right = vline_from.intersections[i_from].right_horizontal();
 				if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
 					// Measure length along the contour.
-	                path.length = unscale<float>(measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to));
+	                path.length = unscale<float>(measure_perimeter_horizontal_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to));
 				}
 			}
 			if (path.length == -1.) {
@@ -1785,6 +1649,24 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
 {
 	std::vector<MonotonousRegion> monotonous_regions;
 
+#ifndef NDEBUG
+	#define SLIC3R_DEBUG_MONOTONOUS_REGIONS
+#endif
+
+#ifdef SLIC3R_DEBUG_MONOTONOUS_REGIONS
+    std::vector<std::vector<std::pair<int, int>>> consumed(segs.size());
+    auto test_overlap = [&consumed](int segment, int low, int high) {
+        for (const std::pair<int, int>& interval : consumed[segment])
+            if ((low >= interval.first && low <= interval.second) ||
+                (interval.first >= low && interval.first <= high))
+                return true;
+        consumed[segment].emplace_back(low, high);
+        return false;
+    };
+#else
+    auto test_overlap = [](int, int, int) { return false; };
+#endif
+
     for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) {
         SegmentedIntersectionLine  &vline_seed = segs[i_vline_seed];
     	for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) {
@@ -1805,6 +1687,7 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
 				region.left.low   = int(left.first  - vline_seed.intersections.data());
 				region.left.high  = int(left.second - vline_seed.intersections.data());
 				region.right      = region.left;
+                assert(! test_overlap(region.left.vline, region.left.low, region.left.high));
 				start->consumed_vertical_up = true;
 				int num_lines = 1;
 				while (++ i_vline < segs.size()) {
@@ -1826,7 +1709,8 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
 					region.right.low   = int(right.first  - vline_right.intersections.data());
 					region.right.high  = int(right.second - vline_right.intersections.data());
 					right.first->consumed_vertical_up = true;
-					++ num_lines;
+                    assert(! test_overlap(region.right.vline, region.right.low, region.right.high));
+                    ++ num_lines;
 					left = right;
 				}
 				// Even number of lines makes the infill zig-zag to exit on the other side of the region than where it starts.
@@ -1898,6 +1782,40 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> &regions, s
             }
 		}
 	}
+
+	// Sometimes a segment may indicate that it connects to a segment on the other side while the other does not.
+    // This may be a valid case if one side contains runs of OUTER_LOW, INNER_LOW, {INNER_HIGH, INNER_LOW}*, INNER_HIGH, OUTER_HIGH,
+    // where the part in the middle does not connect to the other side, but it will be extruded through.
+    for (MonotonousRegion &region : regions) {
+        std::sort(region.left_neighbors.begin(),  region.left_neighbors.end());
+        std::sort(region.right_neighbors.begin(), region.right_neighbors.end());
+    }
+    for (MonotonousRegion &region : regions) {
+        for (MonotonousRegion *neighbor : region.left_neighbors) {
+            auto it = std::lower_bound(neighbor->right_neighbors.begin(), neighbor->right_neighbors.end(), &region);
+            if (it == neighbor->right_neighbors.end() || *it != &region)
+                neighbor->right_neighbors.insert(it, &region);
+        }
+        for (MonotonousRegion *neighbor : region.right_neighbors) {
+            auto it = std::lower_bound(neighbor->left_neighbors.begin(), neighbor->left_neighbors.end(), &region);
+            if (it == neighbor->left_neighbors.end() || *it != &region)
+                neighbor->left_neighbors.insert(it, &region);
+        }
+    }
+
+#ifndef NDEBUG
+    // Verify symmetry of the left_neighbors / right_neighbors.
+    for (MonotonousRegion &region : regions) {
+        for (MonotonousRegion *neighbor : region.left_neighbors) {
+            assert(std::count(region.left_neighbors.begin(), region.left_neighbors.end(), neighbor) == 1);
+            assert(std::find(neighbor->right_neighbors.begin(), neighbor->right_neighbors.end(), &region) != neighbor->right_neighbors.end());
+        }
+        for (MonotonousRegion *neighbor : region.right_neighbors) {
+            assert(std::count(region.right_neighbors.begin(), region.right_neighbors.end(), neighbor) == 1);
+            assert(std::find(neighbor->left_neighbors.begin(), neighbor->left_neighbors.end(), &region) != neighbor->left_neighbors.end());
+        }
+    }
+#endif /* NDEBUG */
 }
 
 // Raad Salman: Algorithms for the Precedence Constrained Generalized Travelling Salesperson Problem
@@ -1936,8 +1854,8 @@ inline void print_ant(const std::string& fmt, TArgs&&... args) {
 static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 	std::vector<MonotonousRegion> &regions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
 {
-	// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed).
-	std::vector<int32_t>			left_neighbors_unprocessed(regions.size(), 0);
+	// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed) + self.
+	std::vector<int32_t>			left_neighbors_unprocessed(regions.size(), 1);
 	// Queue of regions, which have their left neighbors already printed.
 	std::vector<MonotonousRegion*> 	queue;
 	queue.reserve(regions.size());
@@ -1945,7 +1863,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 		if (region.left_neighbors.empty())
 			queue.emplace_back(&region);
 		else
-			left_neighbors_unprocessed[&region - regions.data()] = int(region.left_neighbors.size());
+			left_neighbors_unprocessed[&region - regions.data()] += int(region.left_neighbors.size());
 	// Make copy of structures that need to be initialized at each ant iteration.
 	auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed;
 	auto queue_initial 						= queue;
@@ -1964,6 +1882,64 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 	};
 	std::vector<NextCandidate> next_candidates;
 
+    auto validate_unprocessed = 
+#ifdef NDEBUG
+        []() { return true; };
+#else
+        [&regions, &left_neighbors_unprocessed, &path, &queue]() {
+            std::vector<unsigned char> regions_processed(regions.size(), false);
+            std::vector<unsigned char> regions_in_queue(regions.size(), false);
+            for (const MonotonousRegion *region : queue) {
+            	// This region is not processed yet, his predecessors are processed.
+                assert(left_neighbors_unprocessed[region - regions.data()] == 1);
+                regions_in_queue[region - regions.data()] = true;
+            }
+            for (const MonotonousRegionLink &link : path) {
+                assert(left_neighbors_unprocessed[link.region - regions.data()] == 0);
+                regions_processed[link.region - regions.data()] = true;
+            }
+            for (size_t i = 0; i < regions_processed.size(); ++ i) {
+                assert(! regions_processed[i] || ! regions_in_queue[i]);
+                const MonotonousRegion &region = regions[i];
+                if (regions_processed[i] || regions_in_queue[i]) {
+                    assert(left_neighbors_unprocessed[i] == (regions_in_queue[i] ? 1 : 0));
+                    // All left neighbors should be processed already.
+                    for (const MonotonousRegion *left : region.left_neighbors) {
+                        assert(regions_processed[left - regions.data()]);
+                        assert(left_neighbors_unprocessed[left - regions.data()] == 0);
+                    }
+                } else {
+                    // Some left neihgbor should not be processed yet.
+                    assert(left_neighbors_unprocessed[i] > 1);
+                    size_t num_predecessors_unprocessed = 0;
+                    bool   has_left_last_on_path       = false;
+                    for (const MonotonousRegion* left : region.left_neighbors) {
+                        size_t iprev = left - regions.data();
+                        if (regions_processed[iprev]) {
+                        	assert(left_neighbors_unprocessed[iprev] == 0);
+                            if (left == path.back().region) {
+                                // This region should actually be on queue, but to optimize the queue management
+                                // this item will be processed in the next round by traversing path.back().region->right_neighbors before processing the queue.
+                                assert(! has_left_last_on_path);
+                                has_left_last_on_path = true;
+                                ++ num_predecessors_unprocessed;
+                            }
+                        } else {
+                        	if (regions_in_queue[iprev])
+	                    		assert(left_neighbors_unprocessed[iprev] == 1);
+	                    	else 
+	                    		assert(left_neighbors_unprocessed[iprev] > 1);
+	                    	++ num_predecessors_unprocessed;
+                        }
+                    }
+                    assert(num_predecessors_unprocessed > 0);
+                    assert(left_neighbors_unprocessed[i] == num_predecessors_unprocessed + 1);
+                }
+            }
+            return true;
+        };
+#endif /* NDEBUG */
+
 	// How many times to repeat the ant simulation.
 	constexpr int num_rounds = 10;
 	// With how many ants each of the run will be performed?
@@ -1999,13 +1975,16 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 			path.clear();
 			queue = queue_initial;
 			left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
+            assert(validate_unprocessed());
             // Pick randomly the first from the queue at random orientation.
             int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng);
             path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 });
             *(queue.begin() + first_idx) = std::move(queue.back());
             queue.pop_back();
+            -- left_neighbors_unprocessed[path.back().region - regions.data()];
             assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0);
-			print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)", 
+            assert(validate_unprocessed());
+            print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)",
 				path.back().region->left.vline, 
                 path.back().flipped ? path.back().region->left.high : path.back().region->left.low,
                 path.back().flipped ? path.back().region->left.low  : path.back().region->left.high,
@@ -2014,7 +1993,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
                 path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high);
 
 			while (! queue.empty() || ! path.back().region->right_neighbors.empty()) {
-				// Chain.
+                // Chain.
 				MonotonousRegion 		    &region = *path.back().region;
 				bool 			  			 dir    = path.back().flipped;
 				// Sort by distance to pt.
@@ -2022,8 +2001,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 				next_candidates.reserve(region.right_neighbors.size() * 2);
 				for (MonotonousRegion *next : region.right_neighbors) {
 					int &unprocessed = left_neighbors_unprocessed[next - regions.data()];
-					assert(unprocessed > 0);
-					if (-- unprocessed == 0) {
+					assert(unprocessed > 1);
+					if (-- unprocessed == 1) {
 						// Dependencies of the successive blocks are satisfied.
                         AntPath &path1  	   = path_matrix(region,   dir, *next, false);
                         AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
@@ -2038,6 +2017,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
                 if (num_direct_neighbors == 0) {
                     // Add the queue candidates.
                     for (MonotonousRegion *next : queue) {
+                    	assert(left_neighbors_unprocessed[next - regions.data()] == 1);
                         AntPath &path1  	   = path_matrix(region,   dir, *next, false);
                         AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
                         AntPath &path2 	       = path_matrix(region,   dir, *next, true);
@@ -2068,13 +2048,13 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 					print_ant("\tTaking path at probability threshold %1% of %2%", probability_threshold, total_probability);
 				}
                 // Move the other right neighbors with satisified constraints to the queue.
-                bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors;
                 for (std::vector<NextCandidate>::iterator it_next_candidate = next_candidates.begin(); it_next_candidate != next_candidates.begin() + num_direct_neighbors; ++ it_next_candidate)
                     if ((queue.empty() || it_next_candidate->region != queue.back()) && it_next_candidate->region != take_path->region)
                         queue.emplace_back(it_next_candidate->region);
                 if (take_path - next_candidates.begin() >= num_direct_neighbors) {
                     // Remove the selected path from the queue.
                     auto it = std::find(queue.begin(), queue.end(), take_path->region);
+                    assert(it != queue.end());
                     *it = queue.back();
                     queue.pop_back();
                 }
@@ -2084,6 +2064,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
                 path.back().next         = take_path->link;
                 path.back().next_flipped = take_path->link_flipped;
                 path.emplace_back(MonotonousRegionLink{ next_region, next_dir });
+                assert(left_neighbors_unprocessed[next_region - regions.data()] == 1);
+                left_neighbors_unprocessed[next_region - regions.data()] = 0;
 				print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%) length to prev %7%", 
                     next_region->left.vline, 
                     next_dir ? next_region->left.high : next_region->left.low,
@@ -2103,7 +2085,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
 
 				// Update pheromones along this link.
 				take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit;
-			}
+                assert(validate_unprocessed());
+            }
 
 			// Perform 3-opt local optimization of the path.
 			monotonous_3_opt(path, segs);
@@ -2206,10 +2189,11 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
 		            do {
 		                ++ it;
 						iright = std::max(iright, it->right_horizontal());
-		            } while (it->type != SegmentIntersection::INNER_HIGH);
+                        assert(it->is_inner());
+                    } while (it->type != SegmentIntersection::INNER_HIGH || (it + 1)->type != SegmentIntersection::OUTER_HIGH);
 	                polyline->points.emplace_back(vline.pos, it->pos());
 		            int inext = it->vertical_up();
-		            if (inext == -1)
+                    if (inext == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid)
 		            	break;
 		            const Polygon &poly = poly_with_offset.contour(it->iContour);
 	                assert(it->iContour == vline.intersections[inext].iContour);
@@ -2218,17 +2202,18 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
 	            } 
 	        } else {
 	            // Going down.
-	            assert(it->is_high());
-	            assert(i_intersection > 0);
+                assert(it->is_high());
+                assert(i_intersection > 0);
 	            for (;;) {
 		            do {
 		                -- it;
 		                if (int iright_new = it->right_horizontal(); iright_new != -1)
 		                	iright = iright_new;
-		            } while (it->type != SegmentIntersection::INNER_LOW);
+                        assert(it->is_inner());
+		            } while (it->type != SegmentIntersection::INNER_LOW || (it - 1)->type != SegmentIntersection::OUTER_LOW);
 	                polyline->points.emplace_back(vline.pos, it->pos());
 		            int inext = it->vertical_down();
-		            if (inext == -1)
+		            if (inext == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid)
 		            	break;
 		            const Polygon &poly = poly_with_offset.contour(it->iContour);
 	                assert(it->iContour == vline.intersections[inext].iContour);
@@ -2347,7 +2332,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
 #endif /* SLIC3R_DEBUG */
 
     std::vector<SegmentedIntersectionLine> segs = slice_region_by_vertical_lines(poly_with_offset, n_vlines, x0, line_spacing);
-	connect_segment_intersections_by_contours(poly_with_offset, segs);
+    // Connect by horizontal / vertical links, classify the links based on link_max_length as too long.
+	connect_segment_intersections_by_contours(poly_with_offset, segs, params, link_max_length);
 
 #ifdef SLIC3R_DEBUG
     // Paint the segments and finalize the SVG file.
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 9c7a31d3e..a9175ab0c 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -1123,7 +1123,7 @@ void PrintConfigDef::init_fff_params()
     def->sidetext = L("mm/s");
     def->min = 0;
     def->mode = comAdvanced;
-    def->set_default_value(new ConfigOptionFloat(60));
+    def->set_default_value(new ConfigOptionFloat(15));
 
     def = this->add("layer_gcode", coString);
     def->label = L("After layer change G-code");
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index 01f39872f..314bbf716 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -43,6 +43,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<En
 		assert(next_idx < end_points.size());
 		EndPointType &end_point = end_points[next_idx];
 		end_point.chain_id = 1;
+		assert((next_idx & 1) == 0 || could_reverse_func(next_idx >> 1));
 		out.emplace_back(next_idx / 2, (next_idx & 1) != 0);
 		this_idx = next_idx ^ 1;
 	}
@@ -165,7 +166,9 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals
 		EndPoint *first_point = nullptr;
 		size_t    first_point_idx = std::numeric_limits<size_t>::max();
 		if (start_near != nullptr) {
-            size_t idx = find_closest_point(kdtree, start_near->template cast<double>());
+            size_t idx = find_closest_point(kdtree, start_near->template cast<double>(),
+				// Don't start with a reverse segment, if flipping of the segment is not allowed.
+				[&could_reverse_func](size_t idx) { return (idx & 1) == 0 || could_reverse_func(idx >> 1); });
 			assert(idx < end_points.size());
 			first_point = &end_points[idx];
 			first_point->distance_out = 0.;

From b8e02a54056dce138d17f7cc8449f3d8f88f6b5a Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 28 Apr 2020 18:00:42 +0200
Subject: [PATCH 45/55] Fixed handling of fill_pattern field if not all
 patterns are allowed for the internal infill.

---
 src/slic3r/GUI/Field.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index 12699c37f..186444f66 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -943,7 +943,7 @@ void Choice::set_value(const boost::any& value, bool change_event)
 	}
 	case coEnum: {
 		int val = boost::any_cast<int>(value);
-		if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern")
+		if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "fill_pattern")
 		{
 			if (!m_opt.enum_values.empty()) {
 				std::string key;
@@ -1013,7 +1013,7 @@ boost::any& Choice::get_value()
 	if (m_opt.type == coEnum)
 	{
 		int ret_enum = field->GetSelection(); 
-		if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern")
+		if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "fill_pattern")
 		{
 			if (!m_opt.enum_values.empty()) {
 				std::string key = m_opt.enum_values[ret_enum];
@@ -1025,8 +1025,6 @@ boost::any& Choice::get_value()
 			else
 				m_value = static_cast<InfillPattern>(0);
 		}
-		if (m_opt_id.compare("fill_pattern") == 0)
-			m_value = static_cast<InfillPattern>(ret_enum);
 		else if (m_opt_id.compare("ironing_type") == 0)
 			m_value = static_cast<IroningType>(ret_enum);
 		else if (m_opt_id.compare("gcode_flavor") == 0)

From ef89c73fd557ea2f7b67f62f00360b76de62f370 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 28 Apr 2020 18:28:11 +0200
Subject: [PATCH 46/55] fixing a compilation issue on a buggy GCC on R-PI

---
 src/libslic3r/Fill/FillRectilinear2.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp
index e16d57ad6..b88520b55 100644
--- a/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -133,11 +133,11 @@ struct SegmentIntersection
     // but it could be intersected with OUTER_LOW, INNER_LOW, INNER_HIGH, OUTER_HIGH,
     // and there may be more than one pair of INNER_LOW, INNER_HIGH between OUTER_LOW, OUTER_HIGH.
     enum SegmentIntersectionType : char {
-        OUTER_LOW   = 0,
-        OUTER_HIGH  = 1,
-        INNER_LOW   = 2,
-        INNER_HIGH  = 3,
-        UNKNOWN     = -1
+        UNKNOWN,
+        OUTER_LOW,
+        OUTER_HIGH,
+        INNER_LOW,
+        INNER_HIGH,
     };
     SegmentIntersectionType type { UNKNOWN };
 

From 985225cd387cc6c88549ade2bc81c10c87726070 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 28 Apr 2020 20:39:47 +0200
Subject: [PATCH 47/55] Try to fix build with old wxwidgets builtin png and
 expat

---
 src/CMakeLists.txt | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cdc8e0a13..7509ad474 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -59,9 +59,17 @@ if (SLIC3R_GUI)
 
     include(${wxWidgets_USE_FILE})
 
-    list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX png)
-    list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat)
-    list(APPEND wxWidgets_LIBRARIES ${PNG_LIBRARIES} ${EXPAT_LIBRARIES})
+    string(REGEX MATCH "wxpng" WX_PNG_BUILTIN ${wxWidgets_LIBRARIES})
+    if (PNG_FOUND AND NOT WX_PNG_BUILTIN)
+        list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX png)
+        list(APPEND wxWidgets_LIBRARIES ${PNG_LIBRARIES})
+    endif ()
+
+    string(REGEX MATCH "wxexpat" WX_EXPAT_BUILTIN ${wxWidgets_LIBRARIES})
+    if (EXPAT_FOUND AND NOT WX_EXPAT_BUILTIN)
+        list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat)
+        list(APPEND wxWidgets_LIBRARIES ${EXPAT_LIBRARIES})
+    endif ()
 
 #    list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
     message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
@@ -182,13 +190,13 @@ if (WIN32)
 elseif (XCODE)
     # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level
     add_custom_command(TARGET PrusaSlicer POST_BUILD
-        COMMAND ln -sf "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/resources"
+        COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/resources"
         COMMENT "Symlinking the resources directory into the build tree"
         VERBATIM
     )
 else ()
     add_custom_command(TARGET PrusaSlicer POST_BUILD
-        COMMAND ln -sf "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources"
+        COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources"
         COMMENT "Symlinking the resources directory into the build tree"
         VERBATIM
     )

From 98f0cc0dec4bce83db8e5a53a219032331a50ba2 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 28 Apr 2020 20:43:46 +0200
Subject: [PATCH 48/55] Follow up, make png non required for now

---
 CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 691e234ae..5e8726d08 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -383,7 +383,7 @@ if (NOT EXPAT_FOUND)
     set(EXPAT_LIBRARIES expat)
 endif ()
 
-find_package(PNG REQUIRED)
+find_package(PNG)
 
 find_package(OpenGL REQUIRED)
 

From 6f7fa4bc09ce174350186a47dfc21754bb659d85 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 28 Apr 2020 21:01:09 +0200
Subject: [PATCH 49/55] Fix librt linking for wxWidgets

---
 src/CMakeLists.txt        | 6 ++++++
 src/slic3r/CMakeLists.txt | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7509ad474..281f9e663 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -71,6 +71,12 @@ if (SLIC3R_GUI)
         list(APPEND wxWidgets_LIBRARIES ${EXPAT_LIBRARIES})
     endif ()
 
+    # This is an issue in the new wxWidgets cmake build, doesn't deal with librt
+    find_library(LIBRT rt)
+    if(LIBRT)
+        list(APPEND wxWidgets_LIBRARIES ${LIBRT})
+    endif()
+
 #    list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
     message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
 
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 3711b12a1..2ac8a1ff0 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -204,7 +204,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
 
 encoding_check(libslic3r_gui)
 
-target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES} rt)
+target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES})
 
 if(APPLE)
     target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})

From 2a8c485b32c2342954771328bac27f78f10de444 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 28 Apr 2020 21:11:54 +0200
Subject: [PATCH 50/55] suppress unnecessary test output in release mode

---
 tests/libslic3r/test_marchingsquares.cpp | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/tests/libslic3r/test_marchingsquares.cpp b/tests/libslic3r/test_marchingsquares.cpp
index 9912ff2ca..e9e016157 100644
--- a/tests/libslic3r/test_marchingsquares.cpp
+++ b/tests/libslic3r/test_marchingsquares.cpp
@@ -325,7 +325,7 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
     sla::RasterBase::Resolution res{2560, 1440};
     double                      disp_w = 120.96;
     double                      disp_h = 68.04;
-    
+
     size_t cntr = 0;
     for (ExPolygons &layer : layers) {
         auto rst = create_raster(res, disp_w, disp_h);
@@ -334,26 +334,31 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
             rst.draw(island);
         }
         
+#ifndef NDEBUG
         std::fstream out(objname + std::to_string(cntr) + ".png", std::ios::out);
         out << rst.encode(sla::PNGRasterEncoder{});
         out.close();
+#endif
         
         ExPolygons layer_ = sla::raster_to_polygons(rst);
 //        float delta = scaled(std::min(rst.pixel_dimensions().h_mm,
 //                                      rst.pixel_dimensions().w_mm)) / 2;
         
 //        layer_ = expolygons_simplify(layer_, delta);
-        
+
+#ifndef NDEBUG
         SVG svg(objname +  std::to_string(cntr) + ".svg", BoundingBox(Point{0, 0}, Point{scaled(disp_w), scaled(disp_h)}));
         svg.draw(layer_);
         svg.draw(layer, "green");
         svg.Close();
+#endif
         
         double layera = 0., layera_ = 0.;
         for (auto &p : layer) layera += p.area();
         for (auto &p : layer_) layera_ += p.area();
-        
+#ifndef NDEBUG
         std::cout << cntr++ << std::endl;
+#endif
         double diff = std::abs(layera_ - layera);
         REQUIRE((diff <= 0.1 * layera || diff < scaled<double>(1.) * scaled<double>(1.)));
         

From 9cb59759564a0478f5b07bfa377930e284403d86 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 29 Apr 2020 09:44:46 +0200
Subject: [PATCH 51/55] bring back required switch for opengl with dep_GLEW

---
 deps/GLEW/GLEW.cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/deps/GLEW/GLEW.cmake b/deps/GLEW/GLEW.cmake
index 285b9be4d..5916ce7eb 100644
--- a/deps/GLEW/GLEW.cmake
+++ b/deps/GLEW/GLEW.cmake
@@ -1,6 +1,6 @@
 # We have to check for OpenGL to compile GLEW
 set(OpenGL_GL_PREFERENCE "LEGACY") # to prevent a nasty warning by cmake
-find_package(OpenGL QUIET)
+find_package(OpenGL QUIET REQUIRED)
 
 prusaslicer_add_cmake_project(
   GLEW

From d828a1e80b39eb7bffbcde7b7a48741791a56b43 Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Wed, 29 Apr 2020 10:50:28 +0200
Subject: [PATCH 52/55] single slicer instance

check for other instances during startup
send message with command line arguments if found and terminate
listen for those messages and load objects from paths in messages from them
---
 CMakeLists.txt                     |   3 +
 cmake/modules/FindDBus.cmake       |  59 ++++
 src/PrusaSlicer.cpp                |  13 +-
 src/PrusaSlicer_app_msvc.cpp       |   3 +-
 src/libslic3r/PrintConfig.cpp      |   5 +
 src/slic3r/CMakeLists.txt          |   8 +
 src/slic3r/GUI/AppConfig.cpp       |   3 +
 src/slic3r/GUI/GUI_App.cpp         |  72 +++--
 src/slic3r/GUI/GUI_App.hpp         |   5 +-
 src/slic3r/GUI/InstanceCheck.cpp   | 495 +++++++++++++++++++++++++++++
 src/slic3r/GUI/InstanceCheck.hpp   |  91 ++++++
 src/slic3r/GUI/InstanceCheckMac.h  |   8 +
 src/slic3r/GUI/InstanceCheckMac.mm |  68 ++++
 src/slic3r/GUI/MainFrame.cpp       |   4 +-
 src/slic3r/GUI/Plater.cpp          |  24 ++
 src/slic3r/GUI/Preferences.cpp     |   9 +
 16 files changed, 841 insertions(+), 29 deletions(-)
 create mode 100644 cmake/modules/FindDBus.cmake
 create mode 100644 src/slic3r/GUI/InstanceCheck.cpp
 create mode 100644 src/slic3r/GUI/InstanceCheck.hpp
 create mode 100644 src/slic3r/GUI/InstanceCheckMac.h
 create mode 100644 src/slic3r/GUI/InstanceCheckMac.mm

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e8726d08..48ad5033e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -160,6 +160,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # Boost on Raspberry-Pi does not link to pthreads.
     set(THREADS_PREFER_PTHREAD_FLAG ON)
     find_package(Threads REQUIRED)
+
+    find_package(DBus REQUIRED)
+    include_directories(${DBUS_INCLUDE_DIRS})
 endif()
 
 if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX)
diff --git a/cmake/modules/FindDBus.cmake b/cmake/modules/FindDBus.cmake
new file mode 100644
index 000000000..1d0f29dd7
--- /dev/null
+++ b/cmake/modules/FindDBus.cmake
@@ -0,0 +1,59 @@
+# - Try to find DBus
+# Once done, this will define
+#
+#  DBUS_FOUND - system has DBus
+#  DBUS_INCLUDE_DIRS - the DBus include directories
+#  DBUS_LIBRARIES - link these to use DBus
+#
+# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
+# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FIND_PACKAGE(PkgConfig)
+PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1)
+
+FIND_LIBRARY(DBUS_LIBRARIES
+    NAMES dbus-1
+    HINTS ${PC_DBUS_LIBDIR}
+          ${PC_DBUS_LIBRARY_DIRS}
+)
+
+FIND_PATH(DBUS_INCLUDE_DIR
+    NAMES dbus/dbus.h
+    HINTS ${PC_DBUS_INCLUDEDIR}
+          ${PC_DBUS_INCLUDE_DIRS}
+)
+
+GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH)
+FIND_PATH(DBUS_ARCH_INCLUDE_DIR
+    NAMES dbus/dbus-arch-deps.h
+    HINTS ${PC_DBUS_INCLUDEDIR}
+          ${PC_DBUS_INCLUDE_DIRS}
+          ${_DBUS_LIBRARY_DIR}
+          ${DBUS_INCLUDE_DIR}
+    PATH_SUFFIXES include
+)
+
+SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES)
\ No newline at end of file
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 9efecb50c..dd4ee3b98 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -51,13 +51,14 @@
     #include "slic3r/GUI/GUI.hpp"
     #include "slic3r/GUI/GUI_App.hpp"
     #include "slic3r/GUI/3DScene.hpp"
+    #include "slic3r/GUI/InstanceCheck.hpp" 
+    #include "slic3r/GUI/AppConfig.hpp" 
 #endif /* SLIC3R_GUI */
 
 using namespace Slic3r;
 
 int CLI::run(int argc, char **argv)
 {
-
 #ifdef __WXGTK__
     // On Linux, wxGTK has no support for Wayland, and the app crashes on
     // startup if gtk3 is used. This env var has to be set explicitly to
@@ -524,6 +525,16 @@ int CLI::run(int argc, char **argv)
 #ifdef SLIC3R_GUI
 // #ifdef USE_WX
         GUI::GUI_App *gui = new GUI::GUI_App();
+
+		bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1";
+		if (Slic3r::instance_check(argc, argv, gui_single_instance_setting)) {
+			//TODO: do we have delete gui and other stuff?
+			return -1;
+		}
+		
+		//gui->app_config = app_config;
+		//app_config = nullptr;
+		
 //		gui->autosave = m_config.opt_string("autosave");
         GUI::GUI_App::SetInstance(gui);
         gui->CallAfter([gui, this, &load_configs] {
diff --git a/src/PrusaSlicer_app_msvc.cpp b/src/PrusaSlicer_app_msvc.cpp
index b3d1e8bb4..712cff687 100644
--- a/src/PrusaSlicer_app_msvc.cpp
+++ b/src/PrusaSlicer_app_msvc.cpp
@@ -7,6 +7,8 @@
 #include <shellapi.h>
 #include <wchar.h>
 
+
+
 #ifdef SLIC3R_GUI
 extern "C"
 {
@@ -216,7 +218,6 @@ int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */,
 int wmain(int argc, wchar_t **argv)
 {
 #endif
-
     std::vector<wchar_t*> argv_extended;
     argv_extended.emplace_back(argv[0]);
 
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index a155dff01..43c83fe2b 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -39,6 +39,11 @@ void PrintConfigDef::init_common_params()
 {
     ConfigOptionDef* def;
 
+	def = this->add("single_instance", coBool);
+	def->label = L("Single Instance");
+	def->mode = comAdvanced;
+	def->set_default_value(new ConfigOptionBool(false));
+
     def = this->add("printer_technology", coEnum);
     def->label = L("Printer technology");
     def->tooltip = L("Printer technology");
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 2ac8a1ff0..f910c9602 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -161,6 +161,8 @@ set(SLIC3R_GUI_SOURCES
     GUI/DoubleSlider.hpp
     GUI/ObjectDataViewModel.cpp
     GUI/ObjectDataViewModel.hpp
+    GUI/InstanceCheck.cpp
+    GUI/InstanceCheck.hpp
     Utils/Http.cpp
     Utils/Http.hpp
     Utils/FixModelByWin10.cpp
@@ -195,6 +197,8 @@ if (APPLE)
             GUI/RemovableDriveManagerMM.mm
             GUI/RemovableDriveManagerMM.h
             GUI/Mouse3DHandlerMac.mm
+            GUI/InstanceCheckMac.mm
+            GUI/InstanceCheckMac.h
         )
     FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration)
 
@@ -206,6 +210,10 @@ encoding_check(libslic3r_gui)
 
 target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES})
 
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) 
+endif()
+
 if(APPLE)
     target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
 endif()
diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp
index 616d95147..291949ce9 100644
--- a/src/slic3r/GUI/AppConfig.cpp
+++ b/src/slic3r/GUI/AppConfig.cpp
@@ -69,6 +69,9 @@ void AppConfig::set_defaults()
         set("use_retina_opengl", "1");
 #endif
 
+	if (get("single_instance").empty())
+		set("single_instance", "0");
+
     if (get("remember_output_path").empty())
         set("remember_output_path", "1");
 
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index aee71f16e..c04d7ca16 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -50,6 +50,7 @@
 #include "UpdateDialogs.hpp"
 #include "Mouse3DController.hpp"
 #include "RemovableDriveManager.hpp"
+#include "InstanceCheck.hpp"
 
 #ifdef __WXMSW__
 #include <dbt.h>
@@ -209,6 +210,17 @@ static void register_win32_device_notification_event()
 		}
         return false;
     });
+
+	wxWindow::MSWRegisterMessageHandler(WM_COPYDATA, [](wxWindow* win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
+
+		COPYDATASTRUCT* copy_data_structure = { 0 };
+		copy_data_structure = (COPYDATASTRUCT*)lParam;
+		if (copy_data_structure->dwData == 1) {
+			LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData;
+			Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(boost::nowide::narrow(arguments));
+		}
+		return true;
+		});
 }
 #endif // WIN32
 
@@ -253,7 +265,11 @@ GUI_App::GUI_App()
     , m_imgui(new ImGuiWrapper())
     , m_wizard(nullptr)
 	, m_removable_drive_manager(std::make_unique<RemovableDriveManager>())
-{}
+	, m_other_instance_message_handler(std::make_unique<OtherInstanceMessageHandler>())
+{
+	//app config initializes early becasuse it is used in instance checking in PrusaSlicer.cpp
+	this->init_app_config();
+}
 
 GUI_App::~GUI_App()
 {
@@ -284,6 +300,30 @@ bool GUI_App::init_opengl()
 }
 #endif // ENABLE_NON_STATIC_CANVAS_MANAGER
 
+void GUI_App::init_app_config()
+{
+	// Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
+	SetAppName(SLIC3R_APP_KEY);
+	//SetAppName(SLIC3R_APP_KEY "-beta");
+	SetAppDisplayName(SLIC3R_APP_NAME);
+
+	// Set the Slic3r data directory at the Slic3r XS module.
+	// Unix: ~/ .Slic3r
+	// Windows : "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
+	// Mac : "~/Library/Application Support/Slic3r"
+
+	if (data_dir().empty())
+		set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data());
+
+	if (!app_config)
+		app_config = new AppConfig();
+
+	// load settings
+	app_conf_exists = app_config->exists();
+	if (app_conf_exists) {
+		app_config->load();
+	}
+}
 bool GUI_App::OnInit()
 {
     try {
@@ -301,34 +341,14 @@ bool GUI_App::on_init_inner()
     wxCHECK_MSG(wxDirExists(resources_dir), false,
         wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir));
 
-    // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
-    SetAppName(SLIC3R_APP_KEY);
-//    SetAppName(SLIC3R_APP_KEY "-beta");
-    SetAppDisplayName(SLIC3R_APP_NAME);
-
-// Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
+     // Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
 //    wxSystemOptions::SetOption("msw.staticbox.optimized-paint", 0);
-// Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible
-// performance when working on high resolution multi-display setups.
+    // Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible
+    // performance when working on high resolution multi-display setups.
 //    wxSystemOptions::SetOption("msw.notebook.themed-background", 0);
 
 //     Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION;
-
-    // Set the Slic3r data directory at the Slic3r XS module.
-    // Unix: ~/ .Slic3r
-    // Windows : "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
-    // Mac : "~/Library/Application Support/Slic3r"
-    if (data_dir().empty())
-        set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data());
-
-    app_config = new AppConfig();
-
-    // load settings
-    app_conf_exists = app_config->exists();
-    if (app_conf_exists) {
-        app_config->load();
-    }
-    
+   
     std::string msg = Http::tls_global_init();
     wxRichMessageDialog
         dlg(nullptr,
@@ -407,6 +427,8 @@ bool GUI_App::on_init_inner()
         if (! plater_)
             return;
 
+		//m_other_instance_message_handler->report();
+
         if (app_config->dirty() && app_config->get("autosave") == "1")
             app_config->save();
 
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index bf75eaaa6..b2fcef48b 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -35,6 +35,7 @@ class PrintHostJobQueue;
 
 namespace GUI{
 class RemovableDriveManager;
+class OtherInstanceMessageHandler;
 enum FileType
 {
     FT_STL,
@@ -108,7 +109,7 @@ class GUI_App : public wxApp
     std::unique_ptr<ImGuiWrapper> m_imgui;
     std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
     ConfigWizard* m_wizard;    // Managed by wxWindow tree
-
+	std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler;
 public:
     bool            OnInit() override;
     bool            initialized() const { return m_initialized; }
@@ -196,6 +197,7 @@ public:
     std::vector<Tab *>      tabs_list;
 
 	RemovableDriveManager* removable_drive_manager() { return m_removable_drive_manager.get(); }
+	OtherInstanceMessageHandler* other_instance_message_handler() { return m_other_instance_message_handler.get(); }
 
     ImGuiWrapper* imgui() { return m_imgui.get(); }
 
@@ -211,6 +213,7 @@ public:
 
 private:
     bool            on_init_inner();
+	void            init_app_config();
     void            window_pos_save(wxTopLevelWindow* window, const std::string &name);
     void            window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false);
     void            window_pos_sanitize(wxTopLevelWindow* window);
diff --git a/src/slic3r/GUI/InstanceCheck.cpp b/src/slic3r/GUI/InstanceCheck.cpp
new file mode 100644
index 000000000..e408ce118
--- /dev/null
+++ b/src/slic3r/GUI/InstanceCheck.cpp
@@ -0,0 +1,495 @@
+#include "GUI_App.hpp"
+#include "InstanceCheck.hpp"
+
+#include "boost/nowide/convert.hpp"
+#include <boost/log/trivial.hpp>
+#include <iostream>
+
+#include <fcntl.h>
+#include <errno.h>
+
+#if __linux__
+#include <dbus/dbus.h> /* Pull in all of D-Bus headers. */
+#endif //__linux__
+
+namespace Slic3r {
+namespace instance_check_internal
+{
+	struct CommandLineAnalysis
+	{
+		bool           should_send;
+		std::string    cl_string;
+	};
+	static CommandLineAnalysis process_command_line(int argc, char** argv) //d:\3dmodels\Klapka\Klapka.3mf
+	{
+		CommandLineAnalysis ret { false };
+		if (argc < 2)
+			return ret;
+		ret.cl_string = argv[0];
+		for (size_t i = 1; i < argc; i++) {
+			std::string token = argv[i];
+			if (token == "--single-instance") {
+				ret.should_send = true;
+			} else {
+				ret.cl_string += " ";
+				ret.cl_string += token;
+			}
+		}
+		BOOST_LOG_TRIVIAL(debug) << "single instance: "<< ret.should_send << ". other params: " << ret.cl_string;
+		return ret;
+	}
+} //namespace instance_check_internal
+
+#if _WIN32
+
+namespace instance_check_internal
+{
+	static HWND l_prusa_slicer_hwnd;
+	static BOOL CALLBACK EnumWindowsProc(_In_ HWND   hwnd, _In_ LPARAM lParam)
+	{
+		//checks for other instances of prusaslicer, if found brings it to front and return false to stop enumeration and quit this instance
+		//search is done by classname(wxWindowNR is wxwidgets thing, so probably not unique) and name in window upper panel
+		//other option would be do a mutex and check for its existence
+		TCHAR wndText[1000];
+		TCHAR className[1000];
+		GetClassName(hwnd, className, 1000);
+		GetWindowText(hwnd, wndText, 1000);
+		std::wstring classNameString(className);
+		std::wstring wndTextString(wndText);
+		if (wndTextString.find(L"PrusaSlicer") != std::wstring::npos && classNameString == L"wxWindowNR") {
+			l_prusa_slicer_hwnd = hwnd;
+			ShowWindow(hwnd, SW_SHOWMAXIMIZED);
+			SetForegroundWindow(hwnd);
+			return false;
+		}
+		return true;
+	}
+	static void send_message(const HWND hwnd)
+	{
+		LPWSTR command_line_args = GetCommandLine();
+		//Create a COPYDATASTRUCT to send the information
+		//cbData represents the size of the information we want to send.
+		//lpData represents the information we want to send.
+		//dwData is an ID defined by us(this is a type of ID different than WM_COPYDATA).
+		COPYDATASTRUCT data_to_send = { 0 };
+		data_to_send.dwData = 1;
+		data_to_send.cbData = sizeof(TCHAR) * (wcslen(command_line_args) + 1);
+		data_to_send.lpData = command_line_args;
+
+		SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send);
+	}
+} //namespace instance_check_internal
+
+bool instance_check(int argc, char** argv, bool app_config_single_instance)
+{
+	instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv);
+	if (cla.should_send || app_config_single_instance) {
+		// Call EnumWidnows with own callback. cons: Based on text in the name of the window and class name which is generic.
+		if (!EnumWindows(instance_check_internal::EnumWindowsProc, 0)) {
+			BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
+			instance_check_internal::send_message(instance_check_internal::l_prusa_slicer_hwnd);
+			return true;
+		}
+	}
+	BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
+	return false;
+}
+
+#elif defined(__APPLE__)
+
+namespace instance_check_internal
+{
+	static int get_lock() 
+	{
+		struct flock fl;
+		int fdlock;
+		fl.l_type = F_WRLCK;
+		fl.l_whence = SEEK_SET;
+		fl.l_start = 0;
+		fl.l_len = 1;
+
+		if ((fdlock = open("/tmp/prusaslicer.lock", O_WRONLY | O_CREAT, 0666)) == -1)
+			return 0;
+
+		if (fcntl(fdlock, F_SETLK, &fl) == -1)
+			return 0;
+
+		return 1;
+	}
+} //namespace instance_check_internal
+
+bool instance_check(int argc, char** argv, bool app_config_single_instance)
+{
+	instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv);
+	if (!instance_check_internal::get_lock() && (cla.should_send || app_config_single_instance)) {
+		BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
+		send_message_mac(cla.cl_string);
+		return true;
+	}
+	BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
+	return false;
+}
+
+#elif defined(__linux__)
+
+namespace instance_check_internal
+{
+	static int get_lock() 
+	{
+		struct flock fl;
+		int fdlock;
+		fl.l_type = F_WRLCK;
+		fl.l_whence = SEEK_SET;
+		fl.l_start = 0;
+		fl.l_len = 1;
+
+		if ((fdlock = open("/tmp/prusaslicer.lock", O_WRONLY | O_CREAT, 0666)) == -1)
+			return 0;
+
+		if (fcntl(fdlock, F_SETLK, &fl) == -1)
+			return 0;
+
+		return 1;
+	}
+
+	static void send_message(std::string message_text)
+	{
+		DBusMessage* 	msg;
+	    DBusMessageIter args;
+	    DBusConnection* conn;
+	    DBusError 		err;
+	    dbus_uint32_t 	serial 			= 0;
+	    const char*		sigval 			= message_text.c_str();
+		std::string		interface_name  = "com.prusa3d.prusaslicer.InstanceCheck";
+		std::string   	method_name 	= "AnotherInstace";
+    	std::string		object_name 	= "/com/prusa3d/prusaslicer/InstanceCheck";				
+
+
+	    // initialise the error value
+	    dbus_error_init(&err);
+
+	    // connect to bus, and check for errors (use SESSION bus everywhere!)
+	    conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
+	    if (dbus_error_is_set(&err)) { 
+	    	BOOST_LOG_TRIVIAL(error) << "DBus Connection Error. Message to another instance wont be send.";
+	    	BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: "<< err.message;
+	        dbus_error_free(&err); 
+	        return;
+	    }
+	    if (NULL == conn) { 
+	    	BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send.";
+	        return;
+	    }	    
+
+		//some sources do request interface ownership before constructing msg but i think its wrong.
+
+	    //create new method call message
+		msg = dbus_message_new_method_call(interface_name.c_str(), object_name.c_str(), interface_name.c_str(), method_name.c_str());
+	    if (NULL == msg) { 
+	    	BOOST_LOG_TRIVIAL(error) << "DBus Message is NULL. Message to another instance wont be send.";
+	    	dbus_connection_unref(conn);
+	        return;
+	    }
+	    //the AnotherInstace method is not sending reply.
+	    dbus_message_set_no_reply(msg, TRUE);
+
+	    //append arguments to message
+		if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &sigval, DBUS_TYPE_INVALID)) {
+			BOOST_LOG_TRIVIAL(error) << "Ran out of memory while constructing args for DBus message. Message to another instance wont be send.";
+		    dbus_message_unref(msg);
+		    dbus_connection_unref(conn);
+		    return;
+		}
+
+	    // send the message and flush the connection
+	    if (!dbus_connection_send(conn, msg, &serial)) {
+	        BOOST_LOG_TRIVIAL(error) << "Ran out of memory while sending DBus message.";
+	        dbus_message_unref(msg);
+	        dbus_connection_unref(conn);
+	        return;
+	    }
+	    dbus_connection_flush(conn);
+	   
+		BOOST_LOG_TRIVIAL(trace) << "DBus message sent.";
+	   
+	    // free the message and close the connection
+	    dbus_message_unref(msg);
+	    dbus_connection_unref(conn);
+	}
+} //namespace instance_check_internal
+
+bool instance_check(int argc, char** argv, bool app_config_single_instance)
+{
+	instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv);
+	if (!instance_check_internal::get_lock() && (cla.should_send || app_config_single_instance)) {
+		BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
+		instance_check_internal::send_message(cla.cl_string);
+		return true;
+	}
+	BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
+	return false;
+}
+#endif //_WIN32/__APPLE__/__linux__
+
+
+
+namespace GUI {
+
+wxDEFINE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
+wxDEFINE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
+
+void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
+{
+	assert(!m_initialized);
+	assert(m_callback_evt_handler == nullptr);
+	if (m_initialized) 
+		return;
+
+	m_initialized = true;
+	m_callback_evt_handler = callback_evt_handler;
+
+#if _WIN32 
+	//create_listener_window();
+#endif  //_WIN32
+
+#if defined(__APPLE__)
+	this->register_for_messages();
+#endif //__APPLE__
+
+#ifdef BACKGROUND_MESSAGE_LISTENER
+	m_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen, this)));
+#endif //BACKGROUND_MESSAGE_LISTENER
+}
+void OtherInstanceMessageHandler::shutdown()
+{
+	BOOST_LOG_TRIVIAL(debug) << "message handler shutdown().";
+	assert(m_initialized);
+	if (m_initialized) {
+#if __APPLE__
+		//delete macos implementation
+		this->unregister_for_messages();
+#endif //__APPLE__
+#ifdef BACKGROUND_MESSAGE_LISTENER
+		if (m_thread.joinable()) {
+			// Stop the worker thread, if running.
+			{
+				// Notify the worker thread to cancel wait on detection polling.
+				std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
+				m_stop = true;
+			}
+			m_thread_stop_condition.notify_all();
+			// Wait for the worker thread to stop.
+			m_thread.join();
+			m_stop = false;
+		}
+#endif //BACKGROUND_MESSAGE_LISTENER
+	m_initialized = false;
+	}
+}
+
+namespace MessageHandlerInternal
+{
+   // returns ::path to possible model or empty ::path if input string is not existing path
+	static boost::filesystem::path get_path(const std::string possible_path)
+	{
+		BOOST_LOG_TRIVIAL(debug) << "message part: " << possible_path;
+
+		if (possible_path.empty() || possible_path.size() < 3) {
+			BOOST_LOG_TRIVIAL(debug) << "empty";
+			return boost::filesystem::path();
+		}
+		if (boost::filesystem::exists(possible_path)) {
+			BOOST_LOG_TRIVIAL(debug) << "is path";
+			return boost::filesystem::path(possible_path);
+		} else if (possible_path[0] == '\"') {
+			if(boost::filesystem::exists(possible_path.substr(1, possible_path.size() - 2))) {
+				BOOST_LOG_TRIVIAL(debug) << "is path in quotes";
+				return boost::filesystem::path(possible_path.substr(1, possible_path.size() - 2));
+			}
+		}
+		BOOST_LOG_TRIVIAL(debug) << "is NOT path";
+		return boost::filesystem::path();
+	}
+} //namespace MessageHandlerInternal
+
+void OtherInstanceMessageHandler::handle_message(const std::string message) {
+	std::vector<boost::filesystem::path> paths;
+	auto                                 next_space = message.find(' ');
+	size_t                               last_space = 0;
+	int                                  counter    = 0;
+
+	BOOST_LOG_TRIVIAL(info) << "message from other instance: " << message;
+
+	while (next_space != std::string::npos)
+	{	
+		if (counter != 0) {
+			const std::string possible_path = message.substr(last_space, next_space - last_space);
+			boost::filesystem::path p = MessageHandlerInternal::get_path(possible_path);
+			if(!p.string().empty())
+				paths.emplace_back(p);
+		}
+		last_space = next_space;
+		next_space = message.find(' ', last_space + 1);
+		counter++;
+	}
+	if (counter != 0 ) {
+		boost::filesystem::path p = MessageHandlerInternal::get_path(message.substr(last_space + 1));
+		if (!p.string().empty())
+			paths.emplace_back(p);
+	}
+	if (!paths.empty()) {
+		//wxEvtHandler* evt_handler = wxGetApp().plater(); //assert here?
+		//if (evt_handler) {
+			wxPostEvent(m_callback_evt_handler, LoadFromOtherInstanceEvent(GUI::EVT_LOAD_MODEL_OTHER_INSTANCE, std::vector<boost::filesystem::path>(std::move(paths))));
+		//}
+	}
+}
+
+#ifdef BACKGROUND_MESSAGE_LISTENER
+
+namespace MessageHandlerDBusInternal
+{
+	//reply to introspect makes our DBus object visible for other programs like D-Feet
+	static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) 
+	{
+    	DBusMessage *reply;
+	    const char  *introspection_data =
+	        " <!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
+	        "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">"
+	        " <!-- dbus-sharp 0.8.1 -->"
+	        " <node>"
+	        "   <interface name=\"org.freedesktop.DBus.Introspectable\">"
+	        "     <method name=\"Introspect\">"
+	        "       <arg name=\"data\" direction=\"out\" type=\"s\" />"
+	        "     </method>"
+	        "   </interface>"
+	        "   <interface name=\"com.prusa3d.prusaslicer.InstanceCheck\">"
+	        "     <method name=\"AnotherInstace\">"
+	        "       <arg name=\"data\" direction=\"in\" type=\"s\" />"
+	        "     </method>"
+	        "   </interface>"
+	        " </node>";
+	     
+	    reply = dbus_message_new_method_return(request);
+	    dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_data, DBUS_TYPE_INVALID);
+	    dbus_connection_send(connection, reply, NULL);
+	    dbus_message_unref(reply);
+	}
+	//method AnotherInstance receives message from another PrusaSlicer instance 
+	static void handle_method_another_instance(DBusConnection *connection, DBusMessage *request)
+	{
+	    DBusError     err;
+	    char*         text= "";
+		wxEvtHandler* evt_handler;
+
+	    dbus_error_init(&err);
+	    dbus_message_get_args(request, &err, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
+	    if (dbus_error_is_set(&err)) {
+	    	BOOST_LOG_TRIVIAL(trace) << "Dbus method AnotherInstance received with wrong arguments.";
+	    	dbus_error_free(&err);
+	        return;
+	    }
+	    wxGetApp().other_instance_message_handler()->handle_message(text);
+
+		evt_handler = wxGetApp().plater();
+		if (evt_handler) {
+			wxPostEvent(evt_handler, InstanceGoToFrontEvent(EVT_INSTANCE_GO_TO_FRONT));
+		}
+	}
+	//every dbus message received comes here
+	static DBusHandlerResult handle_dbus_object_message(DBusConnection *connection, DBusMessage *message, void *user_data)
+	{
+		const char* interface_name = dbus_message_get_interface(message);
+	    const char* member_name    = dbus_message_get_member(message);
+
+	    BOOST_LOG_TRIVIAL(trace) << "DBus message received: interface: " << interface_name << ", member: " << member_name;
+
+	    if (0 == strcmp("org.freedesktop.DBus.Introspectable", interface_name) && 0 == strcmp("Introspect", member_name)) {		
+	        respond_to_introspect(connection, message);
+	        return DBUS_HANDLER_RESULT_HANDLED;
+	    } else if (0 == strcmp("com.prusa3d.prusaslicer.InstanceCheck", interface_name) && 0 == strcmp("AnotherInstace", member_name)) {
+	        handle_method_another_instance(connection, message);
+	        return DBUS_HANDLER_RESULT_HANDLED;
+	    } 
+	    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+} //namespace MessageHandlerDBusInternal
+
+void OtherInstanceMessageHandler::listen()
+{
+    DBusConnection* 	 conn;
+    DBusError 			 err;
+    int 				 name_req_val;
+    DBusObjectPathVTable vtable;
+	std::string			 interface_name = "com.prusa3d.prusaslicer.InstanceCheck";
+    std::string			 object_name 	= "/com/prusa3d/prusaslicer/InstanceCheck";
+
+    dbus_error_init(&err);
+
+    // connect to the bus and check for errors (use SESSION bus everywhere!)
+    conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
+    if (dbus_error_is_set(&err)) { 
+	    BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: "<< err.message;
+	    BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
+        dbus_error_free(&err); 
+        return;
+    }
+    if (NULL == conn) { 
+		BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Dbus Messages listening terminating.";
+        return;
+    }
+
+	// request our name on the bus and check for errors
+	name_req_val = dbus_bus_request_name(conn, interface_name.c_str(), DBUS_NAME_FLAG_REPLACE_EXISTING , &err);
+	if (dbus_error_is_set(&err)) {
+	    BOOST_LOG_TRIVIAL(error) << "DBus Request name Error: "<< err.message; 
+	    BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
+	    dbus_error_free(&err); 
+	    dbus_connection_unref(conn);
+	    return;
+	}
+	if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != name_req_val) {
+		BOOST_LOG_TRIVIAL(error) << "Not primary owner of DBus name - probably another PrusaSlicer instance is running.";
+	    BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
+	    dbus_connection_unref(conn);
+	    return;
+	}
+
+	// Set callbacks. Unregister function should not be nessary.
+	vtable.message_function = MessageHandlerDBusInternal::handle_dbus_object_message;
+    vtable.unregister_function = NULL;
+
+    // register new object - this is our access to DBus
+    dbus_connection_try_register_object_path(conn, object_name.c_str(), &vtable, NULL, &err);
+   	if ( dbus_error_is_set(&err) ) {
+   		BOOST_LOG_TRIVIAL(error) << "DBus Register object Error: "<< err.message; 
+	    BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating.";
+	    dbus_connection_unref(conn);
+		dbus_error_free(&err);
+		return;
+	}
+
+	BOOST_LOG_TRIVIAL(trace) << "Dbus object registered. Starting listening for messages.";
+
+	for (;;) {
+		// Wait for 1 second 
+		// Cancellable.
+		{
+			std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
+			m_thread_stop_condition.wait_for(lck, std::chrono::seconds(1), [this] { return m_stop; });
+		}
+		if (m_stop)
+			// Stop the worker thread.
+
+			break;
+		//dispatch should do all the work with incoming messages
+		//second parameter is blocking time that funciton waits for new messages
+		//that is handled here with our own event loop above
+		dbus_connection_read_write_dispatch(conn, 0);
+     }
+     
+   	 dbus_connection_unref(conn);
+}
+#endif //BACKGROUND_MESSAGE_LISTENER
+} // namespace GUI
+} // namespace Slic3r
diff --git a/src/slic3r/GUI/InstanceCheck.hpp b/src/slic3r/GUI/InstanceCheck.hpp
new file mode 100644
index 000000000..4207296a0
--- /dev/null
+++ b/src/slic3r/GUI/InstanceCheck.hpp
@@ -0,0 +1,91 @@
+#ifndef slic3r_InstanceCheck_hpp_
+#define slic3r_InstanceCheck_hpp_
+
+#include "Event.hpp"
+
+#if _WIN32
+#include <windows.h>
+#endif //_WIN32
+
+#include <string>
+
+#include <boost/filesystem.hpp>
+
+#include <boost/thread.hpp>
+#include <tbb/mutex.h>
+#include <condition_variable>
+
+
+
+namespace Slic3r {
+// checks for other running instances and sends them argv,
+// if there is --single-instance argument or AppConfig is set to single_instance=1
+// returns true if this instance should terminate
+bool    instance_check(int argc, char** argv, bool app_config_single_instance);
+
+#if __APPLE__
+// apple implementation of inner functions of instance_check
+// in InstanceCheckMac.mm
+void    send_message_mac(const std::string msg);
+#endif //__APPLE__
+
+namespace GUI {
+
+#if __linux__
+    #define BACKGROUND_MESSAGE_LISTENER
+#endif // __linux__
+
+using LoadFromOtherInstanceEvent = Event<std::vector<boost::filesystem::path>>;
+wxDECLARE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
+
+using InstanceGoToFrontEvent = SimpleEvent;
+wxDECLARE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
+
+class OtherInstanceMessageHandler
+{
+public:
+	OtherInstanceMessageHandler() = default;
+	OtherInstanceMessageHandler(OtherInstanceMessageHandler const&) = delete;
+	void operator=(OtherInstanceMessageHandler const&) = delete;
+	~OtherInstanceMessageHandler() { assert(!m_initialized); }
+
+	// inits listening, on each platform different. On linux starts background thread
+	void    init(wxEvtHandler* callback_evt_handler);
+	// stops listening, on linux stops the background thread
+	void    shutdown();
+
+	//finds paths to models in message(= command line arguments, first should be prusaSlicer executable)
+	//and sends them to plater via LoadFromOtherInstanceEvent
+	//security of messages: from message all existing paths are proccesed to load model 
+	//						win32 - anybody who has hwnd can send message.
+	//						mac - anybody who posts notification with name:@"OtherPrusaSlicerTerminating"
+	//						linux - instrospectable on dbus
+	void    handle_message(const std::string message);
+private:
+	bool                    m_initialized { false };
+	wxEvtHandler*           m_callback_evt_handler { nullptr };
+
+#ifdef BACKGROUND_MESSAGE_LISTENER
+	//worker thread to listen incoming dbus communication
+	boost::thread 			m_thread;
+	std::condition_variable m_thread_stop_condition;
+	mutable std::mutex 		m_thread_stop_mutex;
+	bool 					m_stop{ false };
+	bool					m_start{ true };
+	
+	// background thread method
+	void    listen();
+#endif //BACKGROUND_MESSAGE_LISTENER
+
+#if __APPLE__
+	//implemented at InstanceCheckMac.mm
+	void    register_for_messages();
+	void    unregister_for_messages();
+	// Opaque pointer to RemovableDriveManagerMM
+	void* m_impl_osx;
+#endif //__APPLE__
+
+};
+} // namespace GUI
+} // namespace Slic3r
+#endif // slic3r_InstanceCheck_hpp_
diff --git a/src/slic3r/GUI/InstanceCheckMac.h b/src/slic3r/GUI/InstanceCheckMac.h
new file mode 100644
index 000000000..9931bb679
--- /dev/null
+++ b/src/slic3r/GUI/InstanceCheckMac.h
@@ -0,0 +1,8 @@
+#import <Cocoa/Cocoa.h>
+
+@interface OtherInstanceMessageHandlerMac : NSObject
+
+-(instancetype) init;
+-(void) add_observer;
+-(void) message_update:(NSNotification *)note;
+@end
diff --git a/src/slic3r/GUI/InstanceCheckMac.mm b/src/slic3r/GUI/InstanceCheckMac.mm
new file mode 100644
index 000000000..cbc833c79
--- /dev/null
+++ b/src/slic3r/GUI/InstanceCheckMac.mm
@@ -0,0 +1,68 @@
+#import "InstanceCheck.hpp"
+#import "InstanceCheckMac.h"
+#import "GUI_App.hpp"
+
+@implementation OtherInstanceMessageHandlerMac
+
+-(instancetype) init
+{
+	self = [super init];
+	return self;
+}
+-(void)add_observer
+{
+	NSLog(@"adding observer");
+	[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(message_update:) name:@"OtherPrusaSlicerInstanceMessage" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
+}
+
+-(void)message_update:(NSNotification *)msg
+{
+	//NSLog(@"recieved msg %@", msg);	
+	//demiaturize all windows
+	for(NSWindow* win in [NSApp windows])
+	{
+		if([win isMiniaturized])
+		{
+			[win deminiaturize:self];
+		}
+	}
+	//bring window to front 
+	[[NSApplication sharedApplication] activateIgnoringOtherApps : YES];
+	//pass message  
+	Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(std::string([msg.userInfo[@"data"] UTF8String]));
+}
+
+@end
+
+namespace Slic3r {
+
+void send_message_mac(const std::string msg)
+{
+	NSString *nsmsg = [NSString stringWithCString:msg.c_str() encoding:[NSString defaultCStringEncoding]];
+	//NSLog(@"sending msg %@", nsmsg);
+	[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"OtherPrusaSlicerInstanceMessage" object:nil userInfo:[NSDictionary dictionaryWithObject:nsmsg forKey:@"data"] deliverImmediately:YES];
+}
+
+namespace GUI {
+void OtherInstanceMessageHandler::register_for_messages()
+{
+	m_impl_osx = [[OtherInstanceMessageHandlerMac alloc] init];
+	if(m_impl_osx) {
+		[m_impl_osx add_observer];
+	}
+}
+
+void OtherInstanceMessageHandler::unregister_for_messages()
+{
+	//NSLog(@"unreegistering other instance messages");
+	if (m_impl_osx) {
+        [m_impl_osx release];
+        m_impl_osx = nullptr;
+    } else {
+		NSLog(@"unreegister not required");
+	}
+}
+}//namespace GUI
+}//namespace Slicer
+
+
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index f7d7a6cac..21aad8987 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -26,6 +26,7 @@
 #include "GUI_ObjectList.hpp"
 #include "Mouse3DController.hpp"
 #include "RemovableDriveManager.hpp"
+#include "InstanceCheck.hpp"
 #include "I18N.hpp"
 
 #include <fstream>
@@ -234,7 +235,8 @@ void MainFrame::shutdown()
 
     // Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater.
     wxGetApp().removable_drive_manager()->shutdown();
-
+	//stop listening for messages from other instances
+	wxGetApp().other_instance_message_handler()->shutdown();
     // Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
     // but in rare cases it may not have been called yet.
     wxGetApp().app_config->save();
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index db9e7e59a..d909442b9 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -74,6 +74,8 @@
 #include "../Utils/FixModelByWin10.hpp"
 #include "../Utils/UndoRedo.hpp"
 #include "RemovableDriveManager.hpp"
+#include "InstanceCheck.hpp"
+
 #if ENABLE_NON_STATIC_CANVAS_MANAGER
 #ifdef __APPLE__
 #include "Gizmos/GLGizmosManager.hpp"
@@ -1946,6 +1948,28 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
 
     // Initialize the Undo / Redo stack with a first snapshot.
     this->take_snapshot(_L("New Project"));
+
+    this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent &evt) { 
+		BOOST_LOG_TRIVIAL(debug) << "received load from other instance event ";
+        this->load_files(evt.data, true, true);
+    });
+    this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) { 
+        BOOST_LOG_TRIVIAL(debug) << "prusaslicer window going forward";
+		//this code maximize app window on Fedora
+		wxGetApp().mainframe->Iconize(false);
+        if (wxGetApp().mainframe->IsMaximized())
+            wxGetApp().mainframe->Maximize(true);
+        else
+            wxGetApp().mainframe->Maximize(false);
+		//this code (without code above) maximize window on Ubuntu
+		wxGetApp().mainframe->Restore();  
+		wxGetApp().GetTopWindow()->SetFocus();  // focus on my window
+		wxGetApp().GetTopWindow()->Raise();  // bring window to front
+		wxGetApp().GetTopWindow()->Show(true); // show the window
+
+    });
+	wxGetApp().other_instance_message_handler()->init(this->q);
+
 }
 
 Plater::priv::~priv()
diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp
index 1299d9a48..e2bccb1c7 100644
--- a/src/slic3r/GUI/Preferences.cpp
+++ b/src/slic3r/GUI/Preferences.cpp
@@ -100,6 +100,13 @@ void PreferencesDialog::build()
 	option = Option (def,"show_incompatible_presets");
 	m_optgroup_general->append_single_option_line(option);
 
+	def.label = L("Single Instance");
+	def.type = coBool;
+	def.tooltip = L("If this is enabled, when staring PrusaSlicer and another instance is running, that instance will be reactivated instead.");
+	def.set_default_value(new ConfigOptionBool{ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false });
+	option = Option(def, "single_instance");
+	m_optgroup_general->append_single_option_line(option);
+
 #if __APPLE__
 	def.label = L("Use Retina resolution for the 3D scene");
 	def.type = coBool;
@@ -177,6 +184,8 @@ void PreferencesDialog::accept()
 		app_config->set(it->first, it->second);
 	}
 
+	app_config->save();
+
 	EndModal(wxID_OK);
 
 	// Nothify the UI to update itself from the ini file.

From b91c3d26e332ab828d269e6d2ea1e9d44f3d1e4a Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 29 Apr 2020 12:32:00 +0200
Subject: [PATCH 53/55] Fix build on OSX with new wxWidgets in deps

---
 src/CMakeLists.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 281f9e663..e170ea8d3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -77,6 +77,11 @@ if (SLIC3R_GUI)
         list(APPEND wxWidgets_LIBRARIES ${LIBRT})
     endif()
 
+    # This fixes a OpenGL linking issue on OSX. wxWidgets cmake build includes
+    # wrong libs for opengl in the link line and it does not link to it by himself.
+    # libslic3r_gui will link to opengl anyway, so lets override wx
+    list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX OpenGL)
+
 #    list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
     message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
 

From 8a82e3d5be62ec9b49b9aa9bd2d9f0b7dbb8aa4b Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 29 Apr 2020 14:42:43 +0200
Subject: [PATCH 54/55] Localization fixes.

---
 src/slic3r/GUI/GUI_App.cpp | 14 ++++++--------
 src/slic3r/Utils/Http.cpp  | 30 +++++++++++++++---------------
 2 files changed, 21 insertions(+), 23 deletions(-)

diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index c04d7ca16..73b248c01 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -350,17 +350,15 @@ bool GUI_App::on_init_inner()
 //     Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION;
    
     std::string msg = Http::tls_global_init();
-    wxRichMessageDialog
-        dlg(nullptr,
-            wxString::Format(_(L("%s\nDo you want to continue?")), _(msg)),
-            "PrusaSlicer", wxICON_QUESTION | wxYES_NO);
-    
-    bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes";
     std::string ssl_cert_store = app_config->get("tls_accepted_cert_store_location");
-    ssl_accept = ssl_accept && ssl_cert_store == Http::tls_system_cert_store();
+    bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes" && ssl_cert_store == Http::tls_system_cert_store();
     
-    dlg.ShowCheckBox(_(L("Remember my choice")));
     if (!msg.empty() && !ssl_accept) {
+        wxRichMessageDialog
+            dlg(nullptr,
+                wxString::Format(_(L("%s\nDo you want to continue?")), msg),
+                "PrusaSlicer", wxICON_QUESTION | wxYES_NO);
+        dlg.ShowCheckBox(_(L("Remember my choice")));
         if (dlg.ShowModal() != wxID_YES) return false;
 
         app_config->set("tls_cert_store_accepted",
diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp
index 101654c56..1f1b6d306 100644
--- a/src/slic3r/Utils/Http.cpp
+++ b/src/slic3r/Utils/Http.cpp
@@ -18,10 +18,10 @@
 #include <openssl/x509.h>
 #endif
 
-#define L(s) s
-
-#include "libslic3r/libslic3r.h"
-#include "libslic3r/Utils.hpp"
+#include <libslic3r/libslic3r.h>
+#include <libslic3r/Utils.hpp>
+#include <slic3r/GUI/I18N.hpp>
+#include <slic3r/GUI/format.hpp>
 
 namespace fs = boost::filesystem;
 
@@ -70,26 +70,26 @@ struct CurlGlobalInit
             }
 
             if (!bundle)
-                message = L("Could not detect system SSL certificate store. "
-                            "PrusaSlicer will be unable to establish secure "
-                            "network connections.");
+                message = _u8L("Could not detect system SSL certificate store. "
+                               "PrusaSlicer will be unable to establish secure "
+                               "network connections.");
             else
-                message = string_printf(
-                    L("PrusaSlicer detected system SSL certificate store in: %s"),
+                message = Slic3r::format(
+					_L("PrusaSlicer detected system SSL certificate store in: %1%"),
                     bundle);
 
-            message += string_printf(
-                L("\nTo specify the system certificate store manually, please "
-                  "set the %s environment variable to the correct CA bundle "
-                  "and restart the application."),
+            message += "\n" + Slic3r::format(
+				_L("To specify the system certificate store manually, please "
+                   "set the %1% environment variable to the correct CA bundle "
+                   "and restart the application."),
                 SSL_CA_FILE);
         }
 
 #endif // OPENSSL_CERT_OVERRIDE
         
         if (CURLcode ec = ::curl_global_init(CURL_GLOBAL_DEFAULT)) {
-            message = L("CURL init has failed. PrusaSlicer will be unable to establish "
-                        "network connections. See logs for additional details.");
+            message += _u8L("CURL init has failed. PrusaSlicer will be unable to establish "
+                            "network connections. See logs for additional details.");
             
             BOOST_LOG_TRIVIAL(error) << ::curl_easy_strerror(ec);
         }

From 2875bc685c3cb8b4a70558416bdfd165eb5b41f2 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 29 Apr 2020 15:58:57 +0200
Subject: [PATCH 55/55] Fixed spelling of "sidebar"

---
 src/slic3r/GUI/GLCanvas3D.cpp | 2 +-
 src/slic3r/GUI/MainFrame.cpp  | 2 +-
 src/slic3r/GUI/Plater.cpp     | 6 +++---
 src/slic3r/GUI/Plater.hpp     | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index b03aa6840..e78525d21 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -5261,7 +5261,7 @@ bool GLCanvas3D::_init_collapse_toolbar()
         m_collapse_toolbar.set_tooltip(id, new_tooltip);
         set_tooltip("");
 
-        wxGetApp().plater()->collapse_sidebur(!wxGetApp().plater()->is_sidebar_collapsed());
+        wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed());
     };
 
     if (!m_collapse_toolbar.add_item(item))
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 41c63cc62..d3bd2720e 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -851,7 +851,7 @@ void MainFrame::init_menubar()
             [this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
 #endif // ENABLE_SLOPE_RENDERING
         append_menu_check_item(viewMenu, wxID_ANY, _(L("&Collapse sidebar")), _(L("Collapse sidebar")),
-            [this](wxCommandEvent&) { m_plater->collapse_sidebur(!m_plater->is_sidebar_collapsed()); }, this,
+            [this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
             [this]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
     }
 
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 30ee2a083..02baaa17e 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1620,7 +1620,7 @@ struct Plater::priv
     void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); }
 
     bool is_sidebar_collapsed() const   { return sidebar->is_collapsed(); }
-    void collapse_sidebur(bool show)    { sidebar->collapse(show); }
+    void collapse_sidebar(bool show)    { sidebar->collapse(show); }
 
 #if ENABLE_SLOPE_RENDERING
     bool is_view3D_slope_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_slope_shown(); }
@@ -1928,7 +1928,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
     view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
     view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
     view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
-    view3D_canvas->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebur(!this->q->is_sidebar_collapsed());  });
+    view3D_canvas->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebar(!this->q->is_sidebar_collapsed());  });
     view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
     view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
     view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
@@ -4404,7 +4404,7 @@ bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown
 void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); }
 
 bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); }
-void Plater::collapse_sidebur(bool show) { p->collapse_sidebur(show); }
+void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); }
 
 #if ENABLE_SLOPE_RENDERING
 bool Plater::is_view3D_slope_shown() const { return p->is_view3D_slope_shown(); }
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 72ba45c04..e26b01d1c 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -188,7 +188,7 @@ public:
     void show_view3D_labels(bool show);
 
     bool is_sidebar_collapsed() const;
-    void collapse_sidebur(bool show);
+    void collapse_sidebar(bool show);
 
 #if ENABLE_SLOPE_RENDERING
     bool is_view3D_slope_shown() const;