Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate
This commit is contained in:
commit
4c63d660d1
47 changed files with 1908 additions and 346 deletions
10
resources/icons/mode.svg
Normal file
10
resources/icons/mode.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.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="hex_x5F_green">
|
||||
<g>
|
||||
<polygon fill="#ED6B21" points="8,1 2,5 2,7 2,11 8,15 14,11 14,7 14,5 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 462 B |
61
resources/icons/open_browser.svg
Normal file
61
resources/icons/open_browser.svg
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="16px"
|
||||
height="16px"
|
||||
viewBox="0 0 16 16"
|
||||
style="enable-background:new 0 0 16 16;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="open_browser.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||
id="metadata13"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs11" /><sodipodi:namedview
|
||||
inkscape:document-rotation="0"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1369"
|
||||
id="namedview9"
|
||||
showgrid="false"
|
||||
inkscape:zoom="63"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#808080;}
|
||||
.st1{fill:#ED6B21;}
|
||||
</style>
|
||||
|
||||
|
||||
<path
|
||||
style="fill:#808080"
|
||||
id="path4"
|
||||
d="m 2.9520337,6.2200003 c 0.29,0 0.5200005,-0.2299995 0.5200005,-0.5199995 v -2.0900002 -1.04 c 0,-0.29 0.2299995,-0.5199995 0.5200005,-0.5199995 h 8.8199993 c 0.29,0 0.53,0.5149999 0.53,0.8049998 l 0.05,7.5833331 c 0,0.139999 -0.05,0.269999 -0.150001,0.369999 -0.1,0.1 -0.229999,0.15 -0.369998,0.15 H 9.9520337 v 0 c -0.290001,0 -0.5200005,0.230001 -0.5200005,0.520001 0,0.289999 0.2299995,0.529999 0.5200005,0.529999 h 2.8200003 c 0.01,0 0.01,0 0.02,0 h 0.08 c 0.42,0 0.809999,-0.16 1.109999,-0.46 0.29,-0.3 0.45,-0.7 0.45,-1.12 l -0.04,-7.5933334 C 14.382033,1.9749999 13.682034,1 12.812034,1 H 4.0020329 C 3.1420332,1 2.4320332,1.6999998 2.4320332,2.5699997 v 1.04 2.0900002 c 0,0.2899999 0.2299995,0.5200004 0.5200005,0.5200004 z"
|
||||
class="st0" /><path
|
||||
style="fill:#ed6b21"
|
||||
id="path6"
|
||||
d="m 7.4320332,8.4527254 c 5e-7,-0.2545586 -0.2050605,-0.4596195 -0.452548,-0.452548 H 2.9206921 c -0.2545586,0 -0.3111269,0.1414213 -0.1343503,0.3181982 L 3.8045753,9.336609 c 0.1767766,0.176777 0.1767766,0.4596189 0,0.6363959 L 1.1317118,12.645868 c -0.17677665,0.176777 -0.17677685,0.45962 0.00707,0.643468 l 1.0040927,1.004092 c 0.1838475,0.183847 0.4666904,0.183848 0.6434673,0.0071 l 2.6657925,-2.665793 c 0.1767769,-0.176776 0.459619,-0.176776 0.6363959,0 l 1.0111628,1.011163 c 0.1838479,0.169677 0.3252692,0.113108 0.3323402,-0.13438 z"
|
||||
class="st1" /></svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -70,6 +70,7 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(MachineLimitsUsage)
|
|||
|
||||
static const t_config_enum_values s_keys_map_PrintHostType {
|
||||
{ "prusalink", htPrusaLink },
|
||||
{ "prusaconnect", htPrusaConnect },
|
||||
{ "octoprint", htOctoPrint },
|
||||
{ "duet", htDuet },
|
||||
{ "flashair", htFlashAir },
|
||||
|
@ -1938,6 +1939,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"the kind of the host.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
|
||||
def->enum_values.push_back("prusalink");
|
||||
def->enum_values.push_back("prusaconnect");
|
||||
def->enum_values.push_back("octoprint");
|
||||
def->enum_values.push_back("duet");
|
||||
def->enum_values.push_back("flashair");
|
||||
|
@ -1945,6 +1947,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("repetier");
|
||||
def->enum_values.push_back("mks");
|
||||
def->enum_labels.push_back("PrusaLink");
|
||||
def->enum_labels.push_back("PrusaConnect");
|
||||
def->enum_labels.push_back("OctoPrint");
|
||||
def->enum_labels.push_back("Duet");
|
||||
def->enum_labels.push_back("FlashAir");
|
||||
|
@ -1953,7 +1956,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back("MKS");
|
||||
def->mode = comAdvanced;
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint));
|
||||
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htPrusaLink));
|
||||
|
||||
def = this->add("only_retract_when_crossing_perimeters", coBool);
|
||||
def->label = L("Only retract when crossing perimeters");
|
||||
|
|
|
@ -43,7 +43,7 @@ enum class MachineLimitsUsage {
|
|||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htPrusaLink, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
|
||||
};
|
||||
|
||||
enum AuthorizationType {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "ButtonsDescription.hpp"
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/clrpicker.h>
|
||||
|
@ -7,11 +8,82 @@
|
|||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
//static ModePaletteComboBox::PalettesMap MODE_PALETTES =
|
||||
static std::vector<std::pair<std::string, std::vector<std::string>>> MODE_PALETTES =
|
||||
{
|
||||
{ L("Palette 1"), { "#7DF028", "#FFDC00", "#E70000" } },
|
||||
{ L("Palette 2"), { "#FC766A", "#B0B8B4", "#184A45" } },
|
||||
{ L("Palette 3"), { "#567572", "#964F4C", "#696667" } },
|
||||
{ L("Palette 4"), { "#DA291C", "#56A8CB", "#53A567" } },
|
||||
{ L("Palette 5"), { "#F65058", "#FBDE44", "#28334A" } },
|
||||
{ L("Palette 6"), { "#FF3EA5", "#EDFF00", "#00A4CC" } },
|
||||
{ L("Palette 7"), { "#E95C20", "#006747", "#4F2C1D" } },
|
||||
{ L("Palette 8"), { "#D9514E", "#2A2B2D", "#2DA8D8" } }
|
||||
};
|
||||
|
||||
ModePaletteComboBox::ModePaletteComboBox(wxWindow* parent) :
|
||||
BitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY)
|
||||
{
|
||||
for (const auto& palette : MODE_PALETTES)
|
||||
Append(_(palette.first), *get_bmp(palette.second));
|
||||
}
|
||||
|
||||
void ModePaletteComboBox::UpdateSelection(const std::vector<wxColour> &palette_in)
|
||||
{
|
||||
for (size_t idx = 0; idx < MODE_PALETTES.size(); ++idx ) {
|
||||
const auto& palette = MODE_PALETTES[idx].second;
|
||||
|
||||
bool is_selected = true;
|
||||
for (size_t mode = 0; mode < palette_in.size(); mode++)
|
||||
if (wxColour(palette[mode]) != palette_in[mode]) {
|
||||
is_selected = false;
|
||||
break;
|
||||
}
|
||||
if (is_selected) {
|
||||
Select(int(idx));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Select(-1);
|
||||
}
|
||||
|
||||
BitmapCache& ModePaletteComboBox::bitmap_cache()
|
||||
{
|
||||
static BitmapCache bmps;
|
||||
return bmps;
|
||||
}
|
||||
|
||||
wxBitmapBundle * ModePaletteComboBox::get_bmp(const std::vector<std::string> &palette)
|
||||
{
|
||||
std::string bitmap_key;
|
||||
for (const auto& color : palette)
|
||||
bitmap_key += color + "+";
|
||||
|
||||
const int icon_height = wxOSX ? 10 : 12;
|
||||
|
||||
wxBitmapBundle* bmp_bndl = bitmap_cache().find_bndl(bitmap_key);
|
||||
if (bmp_bndl == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmapBundle*> bmps;
|
||||
for (const auto& color : palette) {
|
||||
bmps.emplace_back(get_bmp_bundle("mode", icon_height, color));
|
||||
bmps.emplace_back(get_empty_bmp_bundle(wxOSX ? 5 : 6, icon_height));
|
||||
}
|
||||
bmp_bndl = bitmap_cache().insert_bndl(bitmap_key, bmps);
|
||||
}
|
||||
|
||||
return bmp_bndl;
|
||||
}
|
||||
|
||||
|
||||
void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWindow* parent, wxColourPickerCtrl** sys_colour, wxColourPickerCtrl** mod_colour)
|
||||
{
|
||||
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 5, 5);
|
||||
|
@ -48,13 +120,81 @@ void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWi
|
|||
|
||||
grid_sizer->Add(*color_picker, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
grid_sizer->Add(btn, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
grid_sizer->Add(sys_label, 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
|
||||
grid_sizer->Add(sys_label, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
};
|
||||
|
||||
add_color(sys_colour, wxGetApp().get_label_clr_sys(), wxGetApp().get_label_default_clr_system(), _L("Value is the same as the system value"));
|
||||
add_color(mod_colour, wxGetApp().get_label_clr_modified(),wxGetApp().get_label_default_clr_modified(), _L("Value was changed and is not equal to the system value or the last saved preset"));
|
||||
}
|
||||
|
||||
void ButtonsDescription::FillSizerWithModeColorDescriptions(
|
||||
wxSizer* sizer, wxWindow* parent,
|
||||
std::vector<wxColourPickerCtrl**> clr_pickers,
|
||||
std::vector<wxColour>& mode_palette)
|
||||
{
|
||||
const int margin = em_unit(parent);
|
||||
|
||||
auto palette_cb = new ModePaletteComboBox(parent);
|
||||
palette_cb->UpdateSelection(mode_palette);
|
||||
|
||||
palette_cb->Bind(wxEVT_COMBOBOX, [clr_pickers, &mode_palette](wxCommandEvent& evt) {
|
||||
const int selection = evt.GetSelection();
|
||||
if (selection < 0)
|
||||
return;
|
||||
const auto& palette = MODE_PALETTES[selection];
|
||||
for (int mode = 0; mode < 3; mode++)
|
||||
if (*clr_pickers[mode]) {
|
||||
wxColour clr = wxColour(palette.second[mode]);
|
||||
(*clr_pickers[mode])->SetColour(clr);
|
||||
mode_palette[mode] = clr;
|
||||
}
|
||||
});
|
||||
|
||||
wxBoxSizer* h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
h_sizer->Add(new wxStaticText(parent, wxID_ANY, _L("Default palette for mode markers") + ": "), 0, wxALIGN_CENTER_VERTICAL);
|
||||
h_sizer->Add(palette_cb, 1, wxEXPAND);
|
||||
|
||||
sizer->Add(h_sizer, 0, wxEXPAND | wxBOTTOM, margin);
|
||||
|
||||
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(9, 5, 5);
|
||||
sizer->Add(grid_sizer, 0, wxEXPAND);
|
||||
|
||||
const std::vector<wxString> names = { _L("Simple"), _CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), _L("Expert") };
|
||||
|
||||
for (size_t mode = 0; mode < names.size(); ++mode) {
|
||||
wxColour& color = mode_palette[mode];
|
||||
|
||||
wxColourPickerCtrl** color_picker = clr_pickers[mode];
|
||||
*color_picker = new wxColourPickerCtrl(parent, wxID_ANY, color);
|
||||
wxGetApp().UpdateDarkUI((*color_picker)->GetPickerCtrl(), true);
|
||||
|
||||
(*color_picker)->Bind(wxEVT_COLOURPICKER_CHANGED, [color_picker, &color, palette_cb, &mode_palette](wxCommandEvent&) {
|
||||
const wxColour new_color = (*color_picker)->GetColour();
|
||||
if (new_color != color) {
|
||||
color = new_color;
|
||||
palette_cb->UpdateSelection(mode_palette);
|
||||
}
|
||||
});
|
||||
|
||||
wxColour def_color = color;
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, "undo");
|
||||
btn->SetToolTip(_L("Revert color"));
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [color_picker, &color, def_color, palette_cb, &mode_palette](wxEvent& event) {
|
||||
color = def_color;
|
||||
(*color_picker)->SetColour(def_color);
|
||||
palette_cb->UpdateSelection(mode_palette);
|
||||
});
|
||||
parent->Bind(wxEVT_UPDATE_UI, [color_picker, def_color](wxUpdateUIEvent& evt) {
|
||||
evt.Enable((*color_picker)->GetColour() != def_color);
|
||||
}, btn->GetId());
|
||||
|
||||
grid_sizer->Add(*color_picker, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
grid_sizer->Add(btn, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
grid_sizer->Add(new wxStaticText(parent, wxID_ANY, names[mode]), 0, wxALIGN_CENTRE_VERTICAL | wxRIGHT, 2*margin);
|
||||
}
|
||||
}
|
||||
|
||||
ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector<Entry> &entries) :
|
||||
wxDialog(parent, wxID_ANY, _(L("Buttons And Text Colors Description")), wxDefaultPosition, wxDefaultSize),
|
||||
m_entries(entries)
|
||||
|
@ -74,13 +214,20 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector<Entry
|
|||
auto description = new wxStaticText(this, wxID_ANY, _(entry.symbol));
|
||||
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL);
|
||||
description = new wxStaticText(this, wxID_ANY, _(entry.explanation));
|
||||
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
|
||||
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL);
|
||||
}
|
||||
|
||||
// Text color description
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
FillSizerWithTextColorDescriptions(sizer, this, &sys_colour, &mod_colour);
|
||||
main_sizer->Add(sizer, 0, wxEXPAND | wxALL, 20);
|
||||
main_sizer->Add(sizer, 0, wxEXPAND | wxALL, 20);
|
||||
|
||||
// Mode color markers description
|
||||
mode_palette = wxGetApp().get_mode_palette();
|
||||
|
||||
wxSizer* mode_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
FillSizerWithModeColorDescriptions(mode_sizer, this, { &simple, &advanced, &expert }, mode_palette);
|
||||
main_sizer->Add(mode_sizer, 0, wxEXPAND | wxALL, 20);
|
||||
|
||||
auto buttons = CreateStdDialogButtonSizer(wxOK|wxCANCEL);
|
||||
main_sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
|
||||
|
@ -89,8 +236,10 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector<Entry
|
|||
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
wxGetApp().set_label_clr_sys(sys_colour->GetColour());
|
||||
wxGetApp().set_label_clr_modified(mod_colour->GetColour());
|
||||
wxGetApp().set_mode_palette(mode_palette);
|
||||
|
||||
EndModal(wxID_OK);
|
||||
});
|
||||
});
|
||||
|
||||
wxGetApp().UpdateDarkUI(btn);
|
||||
wxGetApp().UpdateDarkUI(static_cast<wxButton*>(FindWindowById(wxID_CANCEL, this)));
|
||||
|
|
|
@ -4,16 +4,48 @@
|
|||
#include <wx/dialog.h>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/bmpbndl.h>
|
||||
|
||||
#include "BitmapComboBox.hpp"
|
||||
|
||||
class ScalableBitmap;
|
||||
class wxColourPickerCtrl;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class BitmapCache;
|
||||
|
||||
// ---------------------------------
|
||||
// *** PaletteComboBox ***
|
||||
// ---------------------------------
|
||||
|
||||
// BitmapComboBox used to palets list in GUI Preferences
|
||||
class ModePaletteComboBox : public BitmapComboBox
|
||||
{
|
||||
public:
|
||||
ModePaletteComboBox(wxWindow* parent);
|
||||
~ModePaletteComboBox() = default;
|
||||
|
||||
void UpdateSelection(const std::vector<wxColour>& palette_in);
|
||||
|
||||
protected:
|
||||
// Caching bitmaps for the all bitmaps, used in preset comboboxes
|
||||
static BitmapCache& bitmap_cache();
|
||||
wxBitmapBundle* get_bmp( const std::vector<std::string>& palette);
|
||||
};
|
||||
|
||||
|
||||
class ButtonsDescription : public wxDialog
|
||||
{
|
||||
wxColourPickerCtrl* sys_colour{ nullptr };
|
||||
wxColourPickerCtrl* mod_colour{ nullptr };
|
||||
|
||||
wxColourPickerCtrl* simple { nullptr };
|
||||
wxColourPickerCtrl* advanced { nullptr };
|
||||
wxColourPickerCtrl* expert { nullptr };
|
||||
|
||||
std::vector<wxColour> mode_palette;
|
||||
public:
|
||||
struct Entry {
|
||||
Entry(ScalableBitmap *bitmap, const std::string &symbol, const std::string &explanation) : bitmap(bitmap), symbol(symbol), explanation(explanation) {}
|
||||
|
@ -27,6 +59,9 @@ public:
|
|||
~ButtonsDescription() {}
|
||||
|
||||
static void FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWindow* parent, wxColourPickerCtrl** sys_colour, wxColourPickerCtrl** mod_colour);
|
||||
static void FillSizerWithModeColorDescriptions(wxSizer* sizer, wxWindow* parent,
|
||||
std::vector<wxColourPickerCtrl**> clr_pickers,
|
||||
std::vector<wxColour>& mode_palette);
|
||||
|
||||
private:
|
||||
std::vector<Entry> m_entries;
|
||||
|
|
|
@ -1158,10 +1158,6 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
|||
}
|
||||
case coEnum: {
|
||||
int val = boost::any_cast<int>(value);
|
||||
if (m_opt_id.compare("host_type") == 0 && val != 0 &&
|
||||
m_opt.enum_values.size() > field->GetCount()) // for case, when PrusaLink isn't used as a HostType
|
||||
val--;
|
||||
|
||||
if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "fill_pattern")
|
||||
{
|
||||
std::string key;
|
||||
|
@ -1240,10 +1236,7 @@ boost::any& Choice::get_value()
|
|||
|
||||
if (m_opt.type == coEnum)
|
||||
{
|
||||
if (m_opt_id.compare("host_type") == 0 && m_opt.enum_values.size() > field->GetCount()) {
|
||||
// for case, when PrusaLink isn't used as a HostType
|
||||
m_value = field->GetSelection()+1;
|
||||
} else if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "fill_pattern") {
|
||||
if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "fill_pattern") {
|
||||
const std::string& key = m_opt.enum_values[field->GetSelection()];
|
||||
m_value = int(ConfigOptionEnum<InfillPattern>::get_enum_values().at(key));
|
||||
}
|
||||
|
|
|
@ -1127,7 +1127,7 @@ bool GUI_App::on_init_inner()
|
|||
NppDarkMode::InitDarkMode(init_dark_color_mode, init_sys_menu_enabled);
|
||||
#endif
|
||||
// initialize label colors and fonts
|
||||
init_label_colours();
|
||||
init_ui_colours();
|
||||
init_fonts();
|
||||
|
||||
std::string older_data_dir_path;
|
||||
|
@ -1145,8 +1145,8 @@ bool GUI_App::on_init_inner()
|
|||
if (bool new_dark_color_mode = app_config->get("dark_color_mode") == "1";
|
||||
init_dark_color_mode != new_dark_color_mode) {
|
||||
NppDarkMode::SetDarkMode(new_dark_color_mode);
|
||||
init_label_colours();
|
||||
update_label_colours_from_appconfig();
|
||||
init_ui_colours();
|
||||
update_ui_colours_from_appconfig();
|
||||
}
|
||||
if (bool new_sys_menu_enabled = app_config->get("sys_menu_enabled") == "1";
|
||||
init_sys_menu_enabled != new_sys_menu_enabled)
|
||||
|
@ -1431,10 +1431,16 @@ const wxColour GUI_App::get_label_default_clr_modified()
|
|||
return dark_mode() ? wxColour(253, 111, 40) : wxColour(252, 77, 1);
|
||||
}
|
||||
|
||||
void GUI_App::init_label_colours()
|
||||
const std::vector<std::string> GUI_App::get_mode_default_palette()
|
||||
{
|
||||
return { "#7DF028", "#FFDC00", "#E70000" };
|
||||
}
|
||||
|
||||
void GUI_App::init_ui_colours()
|
||||
{
|
||||
m_color_label_modified = get_label_default_clr_modified();
|
||||
m_color_label_sys = get_label_default_clr_system();
|
||||
m_mode_palette = get_mode_default_palette();
|
||||
|
||||
bool is_dark_mode = dark_mode();
|
||||
#ifdef _WIN32
|
||||
|
@ -1450,19 +1456,30 @@ void GUI_App::init_label_colours()
|
|||
m_color_window_default = is_dark_mode ? wxColour(43, 43, 43) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
}
|
||||
|
||||
void GUI_App::update_label_colours_from_appconfig()
|
||||
void GUI_App::update_ui_colours_from_appconfig()
|
||||
{
|
||||
// load label colors
|
||||
if (app_config->has("label_clr_sys")) {
|
||||
auto str = app_config->get("label_clr_sys");
|
||||
if (str != "")
|
||||
if (!str.empty())
|
||||
m_color_label_sys = wxColour(str);
|
||||
}
|
||||
|
||||
if (app_config->has("label_clr_modified")) {
|
||||
auto str = app_config->get("label_clr_modified");
|
||||
if (str != "")
|
||||
if (!str.empty())
|
||||
m_color_label_modified = wxColour(str);
|
||||
}
|
||||
|
||||
// load mode markers colors
|
||||
if (app_config->has("mode_palette")) {
|
||||
const auto colors = app_config->get("mode_palette");
|
||||
if (!colors.empty()) {
|
||||
m_mode_palette.clear();
|
||||
if (!unescape_strings_cstyle(colors, m_mode_palette))
|
||||
m_mode_palette = get_mode_default_palette();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_App::update_label_colours()
|
||||
|
@ -1649,6 +1666,39 @@ void GUI_App::set_label_clr_sys(const wxColour& clr)
|
|||
app_config->save();
|
||||
}
|
||||
|
||||
const std::string& GUI_App::get_mode_btn_color(int mode_id)
|
||||
{
|
||||
assert(0 <= mode_id && size_t(mode_id) < m_mode_palette.size());
|
||||
return m_mode_palette[mode_id];
|
||||
}
|
||||
|
||||
std::vector<wxColour> GUI_App::get_mode_palette()
|
||||
{
|
||||
return { wxColor(m_mode_palette[0]),
|
||||
wxColor(m_mode_palette[1]),
|
||||
wxColor(m_mode_palette[2]) };
|
||||
}
|
||||
|
||||
void GUI_App::set_mode_palette(const std::vector<wxColour>& palette)
|
||||
{
|
||||
bool save = false;
|
||||
|
||||
for (size_t mode = 0; mode < palette.size(); ++mode) {
|
||||
const wxColour& clr = palette[mode];
|
||||
std::string color_str = clr == wxTransparentColour ? std::string("") : encode_color(ColorRGB(clr.Red(), clr.Green(), clr.Blue()));
|
||||
if (m_mode_palette[mode] != color_str) {
|
||||
m_mode_palette[mode] = color_str;
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (save) {
|
||||
mainframe->update_mode_markers();
|
||||
app_config->set("mode_palette", escape_strings_cstyle(m_mode_palette));
|
||||
app_config->save();
|
||||
}
|
||||
}
|
||||
|
||||
bool GUI_App::tabs_as_menu() const
|
||||
{
|
||||
return app_config->get("tabs_as_menu") == "1"; // || dark_mode();
|
||||
|
|
|
@ -139,6 +139,7 @@ private:
|
|||
wxColour m_color_selected_btn_bg;
|
||||
bool m_force_colors_update { false };
|
||||
#endif
|
||||
std::vector<std::string> m_mode_palette;
|
||||
|
||||
wxFont m_small_font;
|
||||
wxFont m_bold_font;
|
||||
|
@ -194,8 +195,9 @@ public:
|
|||
static bool dark_mode();
|
||||
const wxColour get_label_default_clr_system();
|
||||
const wxColour get_label_default_clr_modified();
|
||||
void init_label_colours();
|
||||
void update_label_colours_from_appconfig();
|
||||
const std::vector<std::string> get_mode_default_palette();
|
||||
void init_ui_colours();
|
||||
void update_ui_colours_from_appconfig();
|
||||
void update_label_colours();
|
||||
// update color mode for window
|
||||
void UpdateDarkUI(wxWindow *window, bool highlited = false, bool just_font = false);
|
||||
|
@ -215,6 +217,9 @@ public:
|
|||
const wxColour& get_label_clr_default() { return m_color_label_default; }
|
||||
const wxColour& get_window_default_clr(){ return m_color_window_default; }
|
||||
|
||||
const std::string& get_mode_btn_color(int mode_id);
|
||||
std::vector<wxColour> get_mode_palette();
|
||||
void set_mode_palette(const std::vector<wxColour> &palette);
|
||||
|
||||
#ifdef _WIN32
|
||||
const wxColour& get_label_highlight_clr() { return m_color_highlight_label_default; }
|
||||
|
|
|
@ -2660,47 +2660,59 @@ void ObjectList::part_selection_changed()
|
|||
disable_ununiform_scale = true;
|
||||
}
|
||||
else if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) {
|
||||
og_name = _L("Group manipulation");
|
||||
|
||||
const Selection& selection = scene_selection();
|
||||
// don't show manipulation panel for case of all Object's parts selection
|
||||
update_and_show_manipulations = !selection.is_single_full_instance();
|
||||
|
||||
if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) {
|
||||
if (selection.is_any_volume() || selection.is_any_modifier())
|
||||
enable_manipulation = !(*m_objects)[obj_idx]->is_cut();
|
||||
else// if (item && m_objects_model->GetItemType(item) == itInstanceRoot)
|
||||
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
|
||||
if (selection.is_single_full_object()) {
|
||||
og_name = _L("Object manipulation");
|
||||
update_and_show_manipulations = true;
|
||||
|
||||
obj_idx = selection.get_object_idx();
|
||||
ModelObject* object = (*m_objects)[obj_idx];
|
||||
m_config = &object->config;
|
||||
disable_ss_manipulation = object->is_cut();
|
||||
}
|
||||
else {
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
if (selection.is_single_full_object() || selection.is_multiple_full_instance() ) {
|
||||
int obj_idx = m_objects_model->GetObjectIdByItem(sels.front());
|
||||
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
|
||||
og_name = _L("Group manipulation");
|
||||
|
||||
// don't show manipulation panel for case of all Object's parts selection
|
||||
update_and_show_manipulations = !selection.is_single_full_instance();
|
||||
|
||||
if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) {
|
||||
if (selection.is_any_volume() || selection.is_any_modifier())
|
||||
enable_manipulation = !(*m_objects)[obj_idx]->is_cut();
|
||||
else// if (item && m_objects_model->GetItemType(item) == itInstanceRoot)
|
||||
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
|
||||
}
|
||||
else if (selection.is_mixed() || selection.is_multiple_full_object()) {
|
||||
std::map<CutObjectBase, std::set<int>> cut_objects;
|
||||
|
||||
// find cut objects
|
||||
for (auto item : sels) {
|
||||
int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
const ModelObject* obj = object(obj_idx);
|
||||
if (obj->is_cut()) {
|
||||
if (cut_objects.find(obj->cut_id) == cut_objects.end())
|
||||
cut_objects[obj->cut_id] = std::set<int>{ obj_idx };
|
||||
else
|
||||
cut_objects.at(obj->cut_id).insert(obj_idx);
|
||||
}
|
||||
else {
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
if (selection.is_single_full_object() || selection.is_multiple_full_instance() ) {
|
||||
int obj_idx = m_objects_model->GetObjectIdByItem(sels.front());
|
||||
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
|
||||
}
|
||||
else if (selection.is_mixed() || selection.is_multiple_full_object()) {
|
||||
std::map<CutObjectBase, std::set<int>> cut_objects;
|
||||
|
||||
// check if selected cut objects are "full selected"
|
||||
for (auto cut_object : cut_objects)
|
||||
if (cut_object.first.check_sum() != cut_object.second.size()) {
|
||||
disable_ss_manipulation = true;
|
||||
break;
|
||||
// find cut objects
|
||||
for (auto item : sels) {
|
||||
int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
const ModelObject* obj = object(obj_idx);
|
||||
if (obj->is_cut()) {
|
||||
if (cut_objects.find(obj->cut_id) == cut_objects.end())
|
||||
cut_objects[obj->cut_id] = std::set<int>{ obj_idx };
|
||||
else
|
||||
cut_objects.at(obj->cut_id).insert(obj_idx);
|
||||
}
|
||||
}
|
||||
disable_ununiform_scale = !cut_objects.empty();
|
||||
|
||||
// check if selected cut objects are "full selected"
|
||||
for (auto cut_object : cut_objects)
|
||||
if (cut_object.first.check_sum() != cut_object.second.size()) {
|
||||
disable_ss_manipulation = true;
|
||||
break;
|
||||
}
|
||||
disable_ununiform_scale = !cut_objects.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -273,10 +273,10 @@ bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
|
|||
if (type == wxEVT_CHAR) {
|
||||
// Char event
|
||||
const auto key = evt.GetUnicodeKey();
|
||||
unsigned int key_u = static_cast<unsigned int>(key);
|
||||
|
||||
// Release BackSpace, Delete, ... when miss wxEVT_KEY_UP event
|
||||
// Already Fixed at begining of new frame
|
||||
// unsigned int key_u = static_cast<unsigned int>(key);
|
||||
//if (key_u >= 0 && key_u < IM_ARRAYSIZE(io.KeysDown) && io.KeysDown[key_u]) {
|
||||
// io.KeysDown[key_u] = false;
|
||||
//}
|
||||
|
|
|
@ -715,6 +715,8 @@ void MainFrame::update_title()
|
|||
|
||||
void MainFrame::init_tabpanel()
|
||||
{
|
||||
wxGetApp().update_ui_colours_from_appconfig();
|
||||
|
||||
// wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10
|
||||
// with multiple high resolution displays connected.
|
||||
#ifdef _MSW_DARK_MODE
|
||||
|
@ -847,7 +849,6 @@ void MainFrame::register_win32_callbacks()
|
|||
|
||||
void MainFrame::create_preset_tabs()
|
||||
{
|
||||
wxGetApp().update_label_colours_from_appconfig();
|
||||
add_created_tab(new TabPrint(m_tabpanel), "cog");
|
||||
add_created_tab(new TabFilament(m_tabpanel), "spool");
|
||||
add_created_tab(new TabSLAPrint(m_tabpanel), "cog");
|
||||
|
@ -1091,7 +1092,9 @@ void MainFrame::on_sys_color_changed()
|
|||
wxBusyCursor wait;
|
||||
|
||||
// update label colors in respect to the system mode
|
||||
wxGetApp().init_label_colours();
|
||||
wxGetApp().init_ui_colours();
|
||||
// but if there are some ui colors in appconfig, they have to be applied
|
||||
wxGetApp().update_ui_colours_from_appconfig();
|
||||
#ifdef __WXMSW__
|
||||
wxGetApp().UpdateDarkUI(m_tabpanel);
|
||||
// m_statusbar->update_dark_ui();
|
||||
|
@ -1114,6 +1117,24 @@ void MainFrame::on_sys_color_changed()
|
|||
this->Refresh();
|
||||
}
|
||||
|
||||
void MainFrame::update_mode_markers()
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
#ifdef _MSW_DARK_MODE
|
||||
// update markers in common mode sizer
|
||||
if (!wxGetApp().tabs_as_menu())
|
||||
dynamic_cast<Notebook*>(m_tabpanel)->UpdateModeMarkers();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// update mode markers on side_bar
|
||||
wxGetApp().sidebar().update_mode_markers();
|
||||
|
||||
// update mode markers in tabs
|
||||
for (auto tab : wxGetApp().tabs_list)
|
||||
tab->update_mode_markers();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators,
|
||||
// as the simple numeric accelerators spoil all numeric data entry.
|
||||
|
|
|
@ -143,6 +143,7 @@ public:
|
|||
~MainFrame() = default;
|
||||
|
||||
void update_layout();
|
||||
void update_mode_markers();
|
||||
|
||||
// Called when closing the application and when switching the application language.
|
||||
void shutdown();
|
||||
|
|
|
@ -109,9 +109,16 @@ void ButtonsListCtrl::OnColorsChanged()
|
|||
for (ScalableButton* btn : m_pageButtons)
|
||||
btn->sys_color_changed();
|
||||
|
||||
m_mode_sizer->sys_color_changed();
|
||||
|
||||
m_sizer->Layout();
|
||||
}
|
||||
|
||||
void ButtonsListCtrl::UpdateModeMarkers()
|
||||
{
|
||||
m_mode_sizer->update_mode_markers();
|
||||
}
|
||||
|
||||
void ButtonsListCtrl::SetSelection(int sel)
|
||||
{
|
||||
if (m_selection == sel)
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
void UpdateMode();
|
||||
void Rescale();
|
||||
void OnColorsChanged();
|
||||
void UpdateModeMarkers();
|
||||
bool InsertPage(size_t n, const wxString& text, bool bSelect = false, const std::string& bmp_name = "");
|
||||
void RemovePage(size_t n);
|
||||
bool SetPageImage(size_t n, const std::string& bmp_name) const;
|
||||
|
@ -251,6 +252,11 @@ public:
|
|||
GetBtnsListCtrl()->OnColorsChanged();
|
||||
}
|
||||
|
||||
void UpdateModeMarkers()
|
||||
{
|
||||
GetBtnsListCtrl()->UpdateModeMarkers();
|
||||
}
|
||||
|
||||
void OnNavigationKey(wxNavigationKeyEvent& event)
|
||||
{
|
||||
if (event.IsWindowChange()) {
|
||||
|
|
|
@ -422,7 +422,7 @@ void NotificationManager::PopNotification::init()
|
|||
count_spaces();
|
||||
count_lines();
|
||||
|
||||
if (m_lines_count == 3)
|
||||
if (m_lines_count == m_normal_lines_count + 1)
|
||||
m_multiline = true;
|
||||
m_notification_start = GLCanvas3D::timestamp_now();
|
||||
if (m_state == EState::Unknown)
|
||||
|
@ -431,8 +431,8 @@ void NotificationManager::PopNotification::init()
|
|||
void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
|
||||
{
|
||||
m_window_height = m_multiline ?
|
||||
std::max(m_lines_count, (size_t)2) * m_line_height :
|
||||
2 * m_line_height;
|
||||
std::max(m_lines_count, m_normal_lines_count) * m_line_height :
|
||||
m_normal_lines_count * m_line_height;
|
||||
m_window_height += 1 * m_line_height; // top and bottom
|
||||
}
|
||||
|
||||
|
@ -444,19 +444,20 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
|
|||
float shift_y = m_line_height;
|
||||
std::string line;
|
||||
|
||||
for (size_t i = 0; i < (m_multiline ? m_endlines.size() : std::min(m_endlines.size(), (size_t)2)); i++) {
|
||||
for (size_t i = 0; i < (m_multiline ? m_endlines.size() : std::min(m_endlines.size(), m_normal_lines_count)); i++) {
|
||||
assert(m_endlines.size() > i && m_text1.size() >= m_endlines[i]);
|
||||
line.clear();
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
||||
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
|
||||
if (i == 1 && m_endlines.size() > 2 && !m_multiline) {
|
||||
if (i == m_normal_lines_count - 1 && m_endlines.size() > m_normal_lines_count && !m_multiline) {
|
||||
// second line with "more" hypertext
|
||||
line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
||||
while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
|
||||
assert(m_normal_lines_count - 2 >= 0);
|
||||
line = m_text1.substr(m_endlines[m_normal_lines_count - 2] + (m_text1[m_endlines[m_normal_lines_count - 2]] == '\n' || m_text1[m_endlines[m_normal_lines_count - 2]] == ' ' ? 1 : 0), m_endlines[m_normal_lines_count - 1] - m_endlines[m_normal_lines_count - 2] - (m_text1[m_endlines[m_normal_lines_count - 2]] == '\n' || m_text1[m_endlines[m_normal_lines_count - 2]] == ' ' ? 1 : 0));
|
||||
while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((" [" + _u8L("More") + "]").c_str()).x) {
|
||||
line = line.substr(0, line.length() - 1);
|
||||
}
|
||||
line += "..";
|
||||
line += " ";//"..";
|
||||
}
|
||||
else {
|
||||
// regural line
|
||||
|
@ -469,18 +470,18 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
|
|||
}
|
||||
}
|
||||
//hyperlink text
|
||||
if (!m_multiline && m_lines_count > 2) {
|
||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + shift_y, _u8L("More"), true);
|
||||
if (!m_multiline && m_lines_count > m_normal_lines_count) {
|
||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + (m_normal_lines_count -1) *shift_y, "[" + _u8L("More") + "]", true);
|
||||
}
|
||||
else if (!m_hypertext.empty()) {
|
||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_endlines.size() - 1) * shift_y, m_hypertext);
|
||||
}
|
||||
|
||||
// text2
|
||||
if (!m_text2.empty() && (m_multiline|| m_lines_count <= 2)) {
|
||||
if (!m_text2.empty() && (m_multiline|| m_lines_count <= m_normal_lines_count)) {
|
||||
starting_y += (m_endlines.size() - 1) * shift_y;
|
||||
last_end = 0;
|
||||
for (size_t i = 0; i < (m_multiline ? m_endlines2.size() : 2); i++) {
|
||||
for (size_t i = 0; i < (m_multiline ? m_endlines2.size() : m_normal_lines_count); i++) {
|
||||
if (i == 0) //first line X is shifted by hypertext
|
||||
ImGui::SetCursorPosX(x_offset + ImGui::CalcTextSize((line + m_hypertext + (line.empty() ? " " : " ")).c_str()).x);
|
||||
else
|
||||
|
@ -514,7 +515,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
|
|||
{
|
||||
if (more)
|
||||
{
|
||||
m_multiline = true;
|
||||
on_more_hypertext_click();
|
||||
set_next_window_size(imgui);
|
||||
}
|
||||
else if (on_text_click()) {
|
||||
|
@ -635,6 +636,11 @@ bool NotificationManager::PopNotification::on_text_click()
|
|||
return m_data.callback(m_evt_handler);
|
||||
return false;
|
||||
}
|
||||
void NotificationManager::PopNotification::on_more_hypertext_click()
|
||||
{
|
||||
m_multiline = true;
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::update(const NotificationData& n)
|
||||
{
|
||||
m_text1 = n.text1;
|
||||
|
@ -875,7 +881,7 @@ void NotificationManager::ProgressBarNotification::init()
|
|||
m_endlines.push_back(0);
|
||||
}
|
||||
if(m_lines_count >= 2) {
|
||||
m_lines_count = 3;
|
||||
m_lines_count = 3;
|
||||
m_multiline = true;
|
||||
while (m_endlines.size() < 3)
|
||||
m_endlines.push_back(m_endlines.back());
|
||||
|
@ -1058,8 +1064,19 @@ void NotificationManager::ProgressBarWithCancelNotification::render_bar(ImGuiWra
|
|||
//------PrintHostUploadNotification----------------
|
||||
void NotificationManager::PrintHostUploadNotification::init()
|
||||
{
|
||||
ProgressBarNotification::init();
|
||||
if (m_state == EState::NotFading && m_uj_state == UploadJobState::PB_COMPLETED)
|
||||
if (is_finished())
|
||||
return;
|
||||
// count_spaces before text - generate_text needs to know width of line
|
||||
count_spaces();
|
||||
generate_text();
|
||||
|
||||
if (m_uj_state == UploadJobState::PB_COMPLETED || m_uj_state == UploadJobState::PB_COMPLETED_WITH_WARNING) {
|
||||
PopNotification::init();
|
||||
m_multiline = m_more_hypertext_used;
|
||||
} else
|
||||
ProgressBarNotification::init();
|
||||
|
||||
if (m_state == EState::NotFading && (m_uj_state == UploadJobState::PB_COMPLETED || m_uj_state == UploadJobState::PB_COMPLETED_WITH_WARNING))
|
||||
m_state = EState::Shown;
|
||||
}
|
||||
void NotificationManager::PrintHostUploadNotification::count_spaces()
|
||||
|
@ -1068,40 +1085,147 @@ void NotificationManager::PrintHostUploadNotification::count_spaces()
|
|||
m_line_height = ImGui::CalcTextSize("A").y;
|
||||
|
||||
m_left_indentation = m_line_height;
|
||||
if (m_uj_state == UploadJobState::PB_ERROR) {
|
||||
if (m_uj_state == UploadJobState::PB_ERROR || m_uj_state == UploadJobState::PB_COMPLETED_WITH_WARNING) {
|
||||
std::string text;
|
||||
text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
//text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker); its always progressbar level (not error or warning)
|
||||
text = (m_uj_state == UploadJobState::PB_ERROR ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
m_left_indentation = picture_width + m_line_height / 2;
|
||||
}
|
||||
m_window_width_offset = m_line_height * 6; //(m_has_cancel_button ? 6 : 4);
|
||||
m_window_width_offset = m_line_height * 6;
|
||||
m_window_width = m_line_height * 25;
|
||||
}
|
||||
bool NotificationManager::PrintHostUploadNotification::push_background_color()
|
||||
{
|
||||
|
||||
if (m_uj_state == UploadJobState::PB_ERROR) {
|
||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||
backcolor.x += 0.3f;
|
||||
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
return true;
|
||||
} else if (m_uj_state == UploadJobState::PB_COMPLETED_WITH_WARNING) {
|
||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||
backcolor.x += 0.3f;
|
||||
backcolor.y += 0.15f;
|
||||
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NotificationManager::PrintHostUploadNotification::generate_text()
|
||||
{
|
||||
auto shorten_to_line = [this](const std::string& text, bool dots) -> std::string {
|
||||
std::string line = text;
|
||||
bool did_shorten = false;
|
||||
while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset) {
|
||||
line = line.substr(0, line.length() - 1);
|
||||
did_shorten = true;
|
||||
}
|
||||
if (did_shorten && dots) {
|
||||
line = line.substr(0, line.length() - 2);
|
||||
line += "...";
|
||||
}
|
||||
return line;
|
||||
};
|
||||
|
||||
// whole text is no longer than 2 lines, filename is max 1 line long.
|
||||
std::string rest = " -> " + (m_original_host == m_host ? m_host : m_host + " (" + m_original_host + ")");
|
||||
std::string line1;
|
||||
if (ImGui::CalcTextSize(m_filename.c_str()).x > m_window_width - m_window_width_offset) {
|
||||
line1 = shorten_to_line(m_filename, true);
|
||||
} else {
|
||||
line1 = shorten_to_line(m_filename + rest, false);
|
||||
size_t over = line1.size() - m_filename.size();
|
||||
if (over < 0)
|
||||
over = 0;
|
||||
if (over < rest.size())
|
||||
rest = rest.substr(over);
|
||||
else if (over >= rest.size())
|
||||
rest.clear();
|
||||
}
|
||||
std::string line2 = shorten_to_line(rest, true);
|
||||
|
||||
// ... if in total that makes more than 1 line, whole notification will behave as 3 line notification (as base height)
|
||||
if (ImGui::CalcTextSize((line1 + line2).c_str()).x > m_window_width - m_window_width_offset)
|
||||
m_normal_lines_count = 3;
|
||||
else
|
||||
m_normal_lines_count = 2;
|
||||
|
||||
if (m_uj_state == UploadJobState::PB_COMPLETED || m_uj_state == UploadJobState::PB_COMPLETED_WITH_WARNING)
|
||||
m_text1 = line1 + line2 + "\n" + _u8L("COMPLETED") + "\n" + m_status_message;
|
||||
else
|
||||
m_text1 = line1 + line2;
|
||||
}
|
||||
|
||||
void NotificationManager::PrintHostUploadNotification::set_percentage(float percent)
|
||||
{
|
||||
m_percentage = percent;
|
||||
if (percent >= 1.0f) {
|
||||
m_uj_state = UploadJobState::PB_COMPLETED;
|
||||
m_has_cancel_button = false;
|
||||
init();
|
||||
if (m_complete_on_100 && percent >= 1.0f) {
|
||||
complete();
|
||||
} else if (percent < 0.0f) {
|
||||
error();
|
||||
} else {
|
||||
} else if (m_uj_state != UploadJobState::PB_COMPLETED && m_uj_state != UploadJobState::PB_COMPLETED_WITH_WARNING){
|
||||
if (m_percentage > 1.f)
|
||||
m_percentage = 1.f;
|
||||
m_uj_state = UploadJobState::PB_PROGRESS;
|
||||
m_has_cancel_button = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::PrintHostUploadNotification::complete()
|
||||
{
|
||||
m_uj_state = UploadJobState::PB_COMPLETED;
|
||||
m_has_cancel_button = false;
|
||||
init();
|
||||
}
|
||||
|
||||
void NotificationManager::PrintHostUploadNotification::complete_with_warning()
|
||||
{
|
||||
m_uj_state = UploadJobState::PB_COMPLETED_WITH_WARNING;
|
||||
m_has_cancel_button = false;
|
||||
init();
|
||||
}
|
||||
|
||||
void NotificationManager::PrintHostUploadNotification::render_text(ImGuiWrapper& imgui,const float win_size_x, const float win_size_y,const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
// If not completed, the text rendering is very similar to progressbar notification except it doesnt use m_multiline to decide.
|
||||
// If completed, whole text is part of m_text_1 and is rendered by PopNotification function.
|
||||
|
||||
if (m_uj_state != UploadJobState::PB_COMPLETED && m_uj_state != UploadJobState::PB_COMPLETED_WITH_WARNING) {
|
||||
// hypertext is not rendered at all. If it is needed, it needs to be added here.
|
||||
// m_endlines should have endline for each line and then for hypertext thus m_endlines[1] should always be in m_text1
|
||||
if (m_endlines[0] != m_endlines[1]) {
|
||||
assert(m_text1.size() >= m_endlines[0] || m_text1.size() >= m_endlines[1]);
|
||||
if (m_endlines[0] > m_text1.size() || m_endlines[1] > m_text1.size())
|
||||
return;
|
||||
// two lines text (what doesnt fit, wont show), one line bar
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(m_line_height / 4);
|
||||
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(m_line_height + m_line_height / 4);
|
||||
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
||||
imgui.text(line.c_str());
|
||||
// uncomment only if close and stop button should be next to each other
|
||||
//if (m_has_cancel_button)
|
||||
// render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
else {
|
||||
assert(m_text1.size() >= m_endlines[0]);
|
||||
if (m_endlines[0] > m_text1.size())
|
||||
return;
|
||||
//one line text, one line bar
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(/*win_size_y / 2 - win_size_y / 6 -*/ m_line_height / 4);
|
||||
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
||||
if (m_has_cancel_button)
|
||||
render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
} else
|
||||
PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
std::string text;
|
||||
|
@ -1117,6 +1241,11 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper&
|
|||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_RESOLVING:
|
||||
text = _u8L("RESOLVING ADDRESS");
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_ERROR:
|
||||
text = _u8L("ERROR");
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
|
@ -1128,9 +1257,8 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper&
|
|||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_COMPLETED:
|
||||
text = _u8L("COMPLETED");
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
|
||||
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_COMPLETED_WITH_WARNING:
|
||||
// whole text with both "COMPLETED" and status message is generated in generate_text()
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1145,8 +1273,23 @@ void NotificationManager::PrintHostUploadNotification::render_left_sign(ImGuiWra
|
|||
ImGui::SetCursorPosX(m_line_height / 3);
|
||||
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
||||
imgui.text(text.c_str());
|
||||
} else if (m_uj_state == UploadJobState::PB_COMPLETED_WITH_WARNING) {
|
||||
std::string text;
|
||||
text = ImGui::WarningMarker;
|
||||
ImGui::SetCursorPosX(m_line_height / 3);
|
||||
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::PrintHostUploadNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
if (m_has_cancel_button)
|
||||
render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
else
|
||||
ProgressBarNotification::render_close_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
|
||||
void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
|
@ -1157,6 +1300,56 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu
|
|||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
|
||||
std::string button_text;
|
||||
button_text = ImGui::CancelButton;
|
||||
|
||||
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y),
|
||||
ImVec2(win_pos.x, win_pos.y + win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)),
|
||||
true))
|
||||
{
|
||||
button_text = ImGui::CancelHoverButton;
|
||||
// tooltip
|
||||
long time_now = wxGetLocalTime();
|
||||
if (m_hover_time > 0 && m_hover_time < time_now) {
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
|
||||
ImGui::BeginTooltip();
|
||||
imgui.text(_u8L("Cancel upload") + " " + GUI::shortkey_ctrl_prefix() + "T");
|
||||
ImGui::EndTooltip();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (m_hover_time == 0)
|
||||
m_hover_time = time_now;
|
||||
}
|
||||
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
|
||||
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
wxGetApp().printhost_job_queue().cancel(m_job_id - 1);
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.35f);
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.125, win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)))
|
||||
{
|
||||
wxGetApp().printhost_job_queue().cancel(m_job_id - 1);
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
|
||||
// bellow is version where both close and stop button are rendered next to each other
|
||||
|
||||
/*
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
std::string button_text;
|
||||
button_text = ImGui::CancelButton;
|
||||
|
||||
|
@ -1197,6 +1390,7 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu
|
|||
wxGetApp().printhost_job_queue().cancel(m_job_id - 1);
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
*/
|
||||
}
|
||||
//------UpdatedItemsInfoNotification-------
|
||||
void NotificationManager::UpdatedItemsInfoNotification::count_spaces()
|
||||
|
@ -1847,7 +2041,10 @@ void NotificationManager::push_upload_job_notification(int id, float filesize,
|
|||
return;
|
||||
}
|
||||
}
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
// filename is created from boost::filesystem::path.string() which if created by path / "file" return \\ as folder division. But could also contain / as folder division. Lets unite this into "/" only.
|
||||
std::string correct_filename(filename);
|
||||
std::replace(correct_filename.begin(), correct_filename.end(), '\\', '/');
|
||||
std::string text = correct_filename + " -> " + host;
|
||||
NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotificationLevel, 10, text };
|
||||
push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, m_evt_handler, 0, id, filesize, filename, host), 0);
|
||||
}
|
||||
|
@ -1879,6 +2076,62 @@ void NotificationManager::set_upload_job_notification_host(int id, const std::st
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::set_upload_job_notification_status(int id, const std::string& status)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PrintHostUpload) {
|
||||
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
|
||||
if (phun->compare_job_id(id)) {
|
||||
phun->set_status(status);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::set_upload_job_notification_comp_on_100(int id, bool comp)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PrintHostUpload) {
|
||||
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
|
||||
if (phun->compare_job_id(id)) {
|
||||
phun->set_complete_on_100(comp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::set_upload_job_notification_completed(int id)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PrintHostUpload) {
|
||||
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
|
||||
if (phun->compare_job_id(id)) {
|
||||
phun->complete();
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::set_upload_job_notification_completed_with_warning(int id)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PrintHostUpload) {
|
||||
PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
|
||||
if (phun->compare_job_id(id)) {
|
||||
phun->complete_with_warning();
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
|
|
|
@ -206,6 +206,10 @@ public:
|
|||
void push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0);
|
||||
void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
|
||||
void set_upload_job_notification_host(int id, const std::string& host);
|
||||
void set_upload_job_notification_status(int id, const std::string& status);
|
||||
void set_upload_job_notification_comp_on_100(int id, bool comp);
|
||||
void set_upload_job_notification_completed(int id);
|
||||
void set_upload_job_notification_completed_with_warning(int id);
|
||||
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
|
||||
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
|
||||
// Download App progress
|
||||
|
@ -352,7 +356,8 @@ private:
|
|||
// Hypertext action, returns true if notification should close.
|
||||
// Action is stored in NotificationData::callback as std::function<bool(wxEvtHandler*)>
|
||||
virtual bool on_text_click();
|
||||
|
||||
// "More" hypertext to show full message
|
||||
virtual void on_more_hypertext_click();
|
||||
// Part of init(), counts horizontal spacing like left indentation
|
||||
virtual void count_spaces();
|
||||
// Part of init(), counts end lines
|
||||
|
@ -413,6 +418,8 @@ private:
|
|||
// True if minimized button is rendered, helps to decide where is area for invisible close button
|
||||
bool m_minimize_b_visible { false };
|
||||
size_t m_lines_count{ 1 };
|
||||
// Number of lines to be shown when m_multiline = false. If m_lines_count = m_normal_lines_count + 1 -> all lines are shown,
|
||||
size_t m_normal_lines_count { 2 };
|
||||
// Target for wxWidgets events sent by clicking on the hyperlink available at some notifications.
|
||||
wxEvtHandler* m_evt_handler;
|
||||
};
|
||||
|
@ -506,7 +513,9 @@ private:
|
|||
PB_PROGRESS,
|
||||
PB_ERROR,
|
||||
PB_CANCELLED,
|
||||
PB_COMPLETED
|
||||
PB_COMPLETED,
|
||||
PB_COMPLETED_WITH_WARNING,
|
||||
PB_RESOLVING
|
||||
};
|
||||
PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize, const std::string& filename, const std::string& host)
|
||||
:ProgressBarNotification(n, id_provider, evt_handler)
|
||||
|
@ -514,37 +523,57 @@ private:
|
|||
, m_file_size(filesize)
|
||||
, m_filename(filename)
|
||||
, m_host(host)
|
||||
, m_original_host(host)
|
||||
{
|
||||
m_has_cancel_button = true;
|
||||
set_percentage(percentage);
|
||||
if (percentage != 0.f)
|
||||
set_percentage(percentage);
|
||||
}
|
||||
static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; }
|
||||
void set_percentage(float percent) override;
|
||||
void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }
|
||||
void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; init(); }
|
||||
bool compare_job_id(const int other_id) const { return m_job_id == other_id; }
|
||||
bool compare_text(const std::string& text) const override { return false; }
|
||||
void set_host(const std::string& host) { m_host = host; update({ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotificationLevel, 10, get_upload_job_text(m_id, m_filename, m_host)}); }
|
||||
void set_host(const std::string& host) { m_host = host; init(); }
|
||||
std::string get_host() const { return m_host; }
|
||||
void set_status(const std::string& status) { m_status_message = status; init(); }
|
||||
void set_complete_on_100(bool val) { m_complete_on_100 = val; }
|
||||
void complete();
|
||||
void complete_with_warning();
|
||||
protected:
|
||||
void init() override;
|
||||
void count_spaces() override;
|
||||
bool push_background_color() override;
|
||||
virtual void render_text(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_bar(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
virtual void render_close_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_cancel_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_left_sign(ImGuiWrapper& imgui) override;
|
||||
|
||||
void generate_text();
|
||||
void on_more_hypertext_click() override { ProgressBarNotification::on_more_hypertext_click(); m_more_hypertext_used = true; }
|
||||
|
||||
// Identifies job in cancel callback
|
||||
int m_job_id;
|
||||
// Size of uploaded size to be displayed in MB
|
||||
float m_file_size;
|
||||
long m_hover_time{ 0 };
|
||||
UploadJobState m_uj_state{ UploadJobState::PB_PROGRESS };
|
||||
UploadJobState m_uj_state{ UploadJobState::PB_RESOLVING };
|
||||
std::string m_filename;
|
||||
std::string m_host;
|
||||
std::string m_original_host; // when hostname is resolved into ip address, we can still display original hostname (that user inserted)
|
||||
std::string m_status_message;
|
||||
bool m_more_hypertext_used { false };
|
||||
// When m_complete_on_100 is set to false - percent >= 1 wont switch to PB_COMPLETED state.
|
||||
bool m_complete_on_100 { true };
|
||||
};
|
||||
|
||||
class SlicingProgressNotification : public ProgressBarNotification
|
||||
|
|
|
@ -45,7 +45,7 @@ OG_CustomCtrl::OG_CustomCtrl( wxWindow* parent,
|
|||
m_v_gap = lround(1.0 * m_em_unit);
|
||||
m_h_gap = lround(0.2 * m_em_unit);
|
||||
|
||||
m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode_simple", wxOSX ? 10 : 12), this);
|
||||
m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode", wxOSX ? 10 : 12), this);
|
||||
m_bmp_blinking_sz = get_bitmap_size(get_bmp_bundle("search_blink"), this);
|
||||
|
||||
init_ctrl_lines();// from og.lines()
|
||||
|
@ -188,7 +188,7 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/)
|
|||
#else
|
||||
GetTextExtent(label, &label_w, &label_h, 0, 0, &m_font);
|
||||
#endif //__WXMSW__
|
||||
h_pos += label_w + 1 + m_h_gap;
|
||||
h_pos += label_w + m_h_gap;
|
||||
}
|
||||
h_pos += (opt.opt.gui_type == ConfigOptionDef::GUIType::legend ? 1 : 3) * blinking_button_width;
|
||||
|
||||
|
@ -199,7 +199,7 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/)
|
|||
if (opt.opt.gui_type == ConfigOptionDef::GUIType::legend)
|
||||
h_pos += 2 * blinking_button_width;
|
||||
|
||||
h_pos += field->getWindow()->GetSize().x;
|
||||
h_pos += field->getWindow()->GetSize().x + m_h_gap;
|
||||
|
||||
if (option_set.size() == 1 && option_set.front().opt.full_width)
|
||||
break;
|
||||
|
@ -418,7 +418,7 @@ void OG_CustomCtrl::msw_rescale()
|
|||
m_v_gap = lround(1.0 * m_em_unit);
|
||||
m_h_gap = lround(0.2 * m_em_unit);
|
||||
|
||||
m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode_simple", wxOSX ? 10 : 12), this);
|
||||
m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode", wxOSX ? 10 : 12), this);
|
||||
m_bmp_blinking_sz = get_bitmap_size(get_bmp_bundle("search_blink"), this);
|
||||
|
||||
init_max_win_width();
|
||||
|
@ -666,9 +666,7 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_mode_bmp(wxDC& dc, wxCoord v_pos)
|
|||
return ctrl->m_h_gap;
|
||||
|
||||
ConfigOptionMode mode = og_line.get_options()[0].opt.mode;
|
||||
const std::string& bmp_name = mode == ConfigOptionMode::comSimple ? "mode_simple" :
|
||||
mode == ConfigOptionMode::comAdvanced ? "mode_advanced" : "mode_expert";
|
||||
wxBitmapBundle* bmp = get_bmp_bundle(bmp_name, wxOSX ? 10 : 12);
|
||||
wxBitmapBundle* bmp = get_bmp_bundle("mode", wxOSX ? 10 : 12, wxGetApp().get_mode_btn_color(mode));
|
||||
wxCoord y_draw = v_pos + lround((height - get_bitmap_size(bmp, ctrl).GetHeight()) / 2);
|
||||
|
||||
if (og_line.get_options().front().opt.gui_type != ConfigOptionDef::GUIType::legend)
|
||||
|
|
|
@ -67,14 +67,15 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::str
|
|||
if (m_parent->get_printer()->has_empty_config()) {
|
||||
// update Print Host upload from the selected preset
|
||||
m_parent->get_printer()->update_from_preset(*preset);
|
||||
// update values in parent (PhysicalPrinterDialog)
|
||||
m_parent->update(true);
|
||||
// update values in parent (PhysicalPrinterDialog)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update PrinterTechnology if it was changed
|
||||
if (m_presets_list->set_printer_technology(preset->printer_technology()))
|
||||
m_parent->set_printer_technology(preset->printer_technology());
|
||||
else
|
||||
m_parent->update(true);
|
||||
|
||||
update_full_printer_name();
|
||||
});
|
||||
|
@ -155,8 +156,7 @@ void PresetForPrinter::on_sys_color_changed()
|
|||
|
||||
PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_name) :
|
||||
DPIDialog(parent, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
m_printer("", wxGetApp().preset_bundle->physical_printers.default_config()),
|
||||
had_all_mk3(!printer_name.empty())
|
||||
m_printer("", wxGetApp().preset_bundle->physical_printers.default_config())
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
#ifndef _WIN32
|
||||
|
@ -241,6 +241,11 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_
|
|||
m_printer_name->SelectAll();
|
||||
}
|
||||
|
||||
const wxSize& bestsize = this->GetBestSize();
|
||||
const wxSize& size = wxSize(bestsize.x, 1.1f * bestsize.y);
|
||||
this->SetSize(size);
|
||||
this->Layout();
|
||||
|
||||
this->CenterOnScreen();
|
||||
}
|
||||
|
||||
|
@ -480,19 +485,32 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
|||
update_host_type(printer_change);
|
||||
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||
m_optgroup->show_field("host_type");
|
||||
if (opt->value == htPrusaLink)
|
||||
{
|
||||
|
||||
// hide PrusaConnect address
|
||||
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
||||
if (wxTextCtrl* temp = dynamic_cast<wxTextCtrl*>(printhost_field->getWindow()); temp && temp->GetValue() == L"https://connect.prusa3d.com") {
|
||||
temp->SetValue(wxString());
|
||||
}
|
||||
}
|
||||
if (opt->value == htPrusaLink) { // PrusaConnect does NOT allow http digest
|
||||
m_optgroup->show_field("printhost_authorization_type");
|
||||
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
|
||||
m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword);
|
||||
for (const char* opt_key : { "printhost_user", "printhost_password" })
|
||||
m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword);
|
||||
m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword);
|
||||
} else {
|
||||
m_optgroup->hide_field("printhost_authorization_type");
|
||||
m_optgroup->show_field("printhost_apikey", true);
|
||||
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
|
||||
m_optgroup->hide_field(opt_key);
|
||||
supports_multiple_printers = opt && opt->value == htRepetier;
|
||||
if (opt->value == htPrusaConnect) { // automatically show default prusaconnect address
|
||||
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
||||
if (wxTextCtrl* temp = dynamic_cast<wxTextCtrl*>(printhost_field->getWindow()); temp && temp->GetValue().IsEmpty()) {
|
||||
temp->SetValue(L"https://connect.prusa3d.com");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -514,7 +532,9 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
|||
|
||||
update_printhost_buttons();
|
||||
|
||||
this->SetSize(this->GetBestSize());
|
||||
const wxSize& bestsize= this->GetBestSize();
|
||||
const wxSize& size = wxSize( bestsize.x, 1.1f * bestsize.y);
|
||||
this->SetSize(size);
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
|
@ -522,17 +542,32 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
|
|||
{
|
||||
if (m_presets.empty())
|
||||
return;
|
||||
bool all_presets_are_from_mk3_family = true;
|
||||
struct {
|
||||
bool supported { true };
|
||||
wxString label;
|
||||
} link, connect;
|
||||
// allowed models are: all MINI, all MK3 and newer, MK2.5 and MK2.5S
|
||||
auto model_supports_prusalink = [](const std::string& model) {
|
||||
return model.size() >= 3 &&
|
||||
(( boost::starts_with(model, "MK") && model[2] > '2' && model[2] <= '9')
|
||||
|| boost::starts_with(model, "MINI")
|
||||
|| boost::starts_with(model, "MK2.5")
|
||||
//|| boost::starts_with(model, "MK2.5S")
|
||||
);
|
||||
};
|
||||
// allowed models are: all MK3/S and MK2.5/S
|
||||
auto model_supports_prusaconnect = [](const std::string& model) {
|
||||
return model.size() >= 3 &&
|
||||
(boost::starts_with(model, "MK3")
|
||||
|| boost::starts_with(model, "MK2.5")
|
||||
);
|
||||
};
|
||||
|
||||
// set all_presets_are_prusalink_supported
|
||||
for (PresetForPrinter* prstft : m_presets) {
|
||||
std::string preset_name = prstft->get_preset_name();
|
||||
if (Preset* preset = wxGetApp().preset_bundle->printers.find_preset(preset_name)) {
|
||||
std::string model_id = preset->config.opt_string("printer_model");
|
||||
auto model_supports_prusalink = [](const std::string &model) {
|
||||
return model.size() >= 3 &&
|
||||
((boost::starts_with(model, "MK") && model[2] > '2' && model[2] <= '9') ||
|
||||
boost::starts_with(model, "MINI"));
|
||||
};
|
||||
std::string model_id = preset->config.opt_string("printer_model");
|
||||
if (preset->vendor) {
|
||||
if (preset->vendor->name == "Prusa Research") {
|
||||
const std::vector<VendorProfile::PrinterModel>& models = preset->vendor->models;
|
||||
|
@ -541,37 +576,73 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
|
|||
if (it != models.end() && model_supports_prusalink(it->family))
|
||||
continue;
|
||||
}
|
||||
} else if (model_supports_prusalink(model_id))
|
||||
}
|
||||
else if (model_supports_prusalink(model_id))
|
||||
continue;
|
||||
}
|
||||
all_presets_are_from_mk3_family = false;
|
||||
link.supported = false;
|
||||
break;
|
||||
}
|
||||
|
||||
Field* ht = m_optgroup->get_field("host_type");
|
||||
// set all_presets_are_prusaconnect_supported
|
||||
for (PresetForPrinter* prstft : m_presets) {
|
||||
std::string preset_name = prstft->get_preset_name();
|
||||
Preset* preset = wxGetApp().preset_bundle->printers.find_preset(preset_name);
|
||||
if (!preset) {
|
||||
connect.supported = false;
|
||||
break;
|
||||
}
|
||||
std::string model_id = preset->config.opt_string("printer_model");
|
||||
if (preset->vendor && preset->vendor->name != "Prusa Research") {
|
||||
connect.supported = false;
|
||||
break;
|
||||
}
|
||||
if (preset->vendor && preset->vendor->name != "Prusa Research") {
|
||||
connect.supported = false;
|
||||
break;
|
||||
}
|
||||
// model id should be enough for this case
|
||||
if (!model_supports_prusaconnect(model_id)) {
|
||||
connect.supported = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Field* ht = m_optgroup->get_field("host_type");
|
||||
wxArrayString types;
|
||||
int last_in_conf = m_config->option("host_type")->getInt(); // this is real position in last choice
|
||||
|
||||
|
||||
// Append localized enum_labels
|
||||
assert(ht->m_opt.enum_labels.size() == ht->m_opt.enum_values.size());
|
||||
for (size_t i = 0; i < ht->m_opt.enum_labels.size(); i++) {
|
||||
if (ht->m_opt.enum_values[i] == "prusalink" && !all_presets_are_from_mk3_family)
|
||||
continue;
|
||||
if (ht->m_opt.enum_values[i] == "prusalink"){
|
||||
link.label = _(ht->m_opt.enum_labels[i]);
|
||||
if (!link.supported)
|
||||
continue;
|
||||
}
|
||||
if (ht->m_opt.enum_values[i] == "prusaconnect") {
|
||||
connect.label = _(ht->m_opt.enum_labels[i]);
|
||||
if (!connect.supported)
|
||||
continue;
|
||||
}
|
||||
|
||||
types.Add(_(ht->m_opt.enum_labels[i]));
|
||||
}
|
||||
|
||||
Choice* choice = dynamic_cast<Choice*>(ht);
|
||||
choice->set_values(types);
|
||||
auto set_to_choice_and_config = [this, choice](PrintHostType type) {
|
||||
choice->set_value(static_cast<int>(type));
|
||||
int index_in_choice = (printer_change ? 0 : last_in_conf);
|
||||
choice->set_value(index_in_choice);
|
||||
if (link.supported && link.label == _(ht->m_opt.enum_labels[index_in_choice]))
|
||||
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaLink));
|
||||
else if (link.supported && link.label == _(ht->m_opt.enum_labels[index_in_choice]))
|
||||
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaConnect));
|
||||
else {
|
||||
int host_type = std::clamp(index_in_choice + ((int)ht->m_opt.enum_values.size() - (int)types.size()), 0, (int)ht->m_opt.enum_values.size() - 1);
|
||||
PrintHostType type = static_cast<PrintHostType>(host_type);
|
||||
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(type));
|
||||
};
|
||||
if ((printer_change && all_presets_are_from_mk3_family) || (!had_all_mk3 && all_presets_are_from_mk3_family))
|
||||
set_to_choice_and_config(htPrusaLink);
|
||||
else if ((printer_change && !all_presets_are_from_mk3_family) || (!all_presets_are_from_mk3_family && m_config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value == htPrusaLink))
|
||||
set_to_choice_and_config(htOctoPrint);
|
||||
else
|
||||
choice->set_value(m_config->option("host_type")->getInt());
|
||||
had_all_mk3 = all_presets_are_from_mk3_family;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -591,7 +662,7 @@ void PhysicalPrinterDialog::update_full_printer_names()
|
|||
void PhysicalPrinterDialog::set_printer_technology(PrinterTechnology pt)
|
||||
{
|
||||
m_config->set_key_value("printer_technology", new ConfigOptionEnum<PrinterTechnology>(pt));
|
||||
update();
|
||||
update(true);
|
||||
}
|
||||
|
||||
PrinterTechnology PhysicalPrinterDialog::get_printer_technology()
|
||||
|
|
|
@ -99,7 +99,6 @@ protected:
|
|||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
void on_sys_color_changed() override;
|
||||
|
||||
bool had_all_mk3;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1183,6 +1183,12 @@ void Sidebar::sys_color_changed()
|
|||
p->searcher.dlg_sys_color_changed();
|
||||
}
|
||||
|
||||
void Sidebar::update_mode_markers()
|
||||
{
|
||||
if (p->mode_sizer)
|
||||
p->mode_sizer->update_mode_markers();
|
||||
}
|
||||
|
||||
void Sidebar::search()
|
||||
{
|
||||
p->searcher.search();
|
||||
|
@ -6469,12 +6475,31 @@ void Plater::send_gcode()
|
|||
wxBusyCursor wait;
|
||||
upload_job.printhost->get_groups(groups);
|
||||
}
|
||||
|
||||
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups);
|
||||
// PrusaLink specific: Query the server for the list of file groups.
|
||||
wxArrayString storage;
|
||||
{
|
||||
wxBusyCursor wait;
|
||||
try {
|
||||
upload_job.printhost->get_storage(storage);
|
||||
} catch (const Slic3r::IOError& ex) {
|
||||
show_error(this, ex.what(), false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups, storage);
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
upload_job.upload_data.upload_path = dlg.filename();
|
||||
upload_job.upload_data.post_action = dlg.post_action();
|
||||
upload_job.upload_data.group = dlg.group();
|
||||
upload_job.upload_data.storage = dlg.storage();
|
||||
|
||||
// Show "Is printer clean" dialog for PrusaConnect - Upload and print.
|
||||
if (std::string(upload_job.printhost->get_name()) == "PrusaConnect" && upload_job.upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
|
||||
GUI::MessageDialog dlg(nullptr, _L("Is the printer ready? Is the print sheet in place, empty and clean?"), _L("Upload and Print"), wxOK | wxCANCEL);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
p->export_gcode(fs::path(), false, std::move(upload_job));
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ public:
|
|||
void update_reslice_btn_tooltip() const;
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
void update_mode_markers();
|
||||
void search();
|
||||
void jump_to_option(size_t selected);
|
||||
void jump_to_option(const std::string& opt_key, Preset::Type type, const std::wstring& category);
|
||||
|
|
|
@ -80,9 +80,15 @@ void PreferencesDialog::show(const std::string& highlight_opt_key /*= std::strin
|
|||
m_use_custom_toolbar_size = get_app_config()->get("use_custom_toolbar_size") == "1";
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
// update colors for color pickers
|
||||
// update colors for color pickers of the labels
|
||||
update_color(m_sys_colour, wxGetApp().get_label_clr_sys());
|
||||
update_color(m_mod_colour, wxGetApp().get_label_clr_modified());
|
||||
|
||||
// update color pickers for mode palette
|
||||
const auto palette = wxGetApp().get_mode_palette();
|
||||
std::vector<wxColourPickerCtrl*> color_pickres = {m_mode_simple, m_mode_advanced, m_mode_expert};
|
||||
for (size_t mode = 0; mode < color_pickres.size(); ++mode)
|
||||
update_color(color_pickres[mode], palette[mode]);
|
||||
}
|
||||
|
||||
this->ShowModal();
|
||||
|
@ -506,6 +512,7 @@ void PreferencesDialog::build()
|
|||
|
||||
create_settings_mode_widget();
|
||||
create_settings_text_color_widget();
|
||||
create_settings_mode_color_widget();
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
// Add "Render" tab
|
||||
|
@ -660,6 +667,7 @@ void PreferencesDialog::accept(wxEvent&)
|
|||
if (wxGetApp().is_editor()) {
|
||||
wxGetApp().set_label_clr_sys(m_sys_colour->GetColour());
|
||||
wxGetApp().set_label_clr_modified(m_mod_colour->GetColour());
|
||||
wxGetApp().set_mode_palette(m_mode_palette);
|
||||
}
|
||||
|
||||
EndModal(wxID_OK);
|
||||
|
@ -935,6 +943,33 @@ void PreferencesDialog::create_settings_text_color_widget()
|
|||
append_preferences_option_to_searcer(m_optgroup_gui, opt_key, title);
|
||||
}
|
||||
|
||||
void PreferencesDialog::create_settings_mode_color_widget()
|
||||
{
|
||||
wxWindow* parent = m_optgroup_gui->parent();
|
||||
|
||||
wxString title = L("Mode markers");
|
||||
wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title));
|
||||
wxGetApp().UpdateDarkUI(stb);
|
||||
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
|
||||
std::string opt_key = "mode_markers";
|
||||
m_blinkers[opt_key] = new BlinkingBitmap(parent);
|
||||
|
||||
wxSizer* stb_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
|
||||
|
||||
// Mode color markers description
|
||||
m_mode_palette = wxGetApp().get_mode_palette();
|
||||
ButtonsDescription::FillSizerWithModeColorDescriptions(stb_sizer, parent, { &m_mode_simple, &m_mode_advanced, &m_mode_expert }, m_mode_palette);
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2);
|
||||
sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
|
||||
|
||||
append_preferences_option_to_searcer(m_optgroup_gui, opt_key, title);
|
||||
}
|
||||
|
||||
void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key)
|
||||
{
|
||||
if (m_blinkers.find(opt_key) != m_blinkers.end())
|
||||
|
|
|
@ -48,6 +48,12 @@ class PreferencesDialog : public DPIDialog
|
|||
|
||||
wxColourPickerCtrl* m_sys_colour {nullptr};
|
||||
wxColourPickerCtrl* m_mod_colour {nullptr};
|
||||
|
||||
std::vector<wxColour> m_mode_palette;
|
||||
wxColourPickerCtrl* m_mode_simple { nullptr };
|
||||
wxColourPickerCtrl* m_mode_advanced { nullptr };
|
||||
wxColourPickerCtrl* m_mode_expert { nullptr };
|
||||
|
||||
wxBookCtrlBase* tabs {nullptr};
|
||||
|
||||
bool isOSX {false};
|
||||
|
@ -81,6 +87,7 @@ protected:
|
|||
void create_icon_size_slider();
|
||||
void create_settings_mode_widget();
|
||||
void create_settings_text_color_widget();
|
||||
void create_settings_mode_color_widget();
|
||||
void init_highlighter(const t_config_option_key& opt_key);
|
||||
std::vector<ConfigOptionsGroup*> optgroups();
|
||||
|
||||
|
|
|
@ -344,6 +344,14 @@ void PresetComboBox::add_physical_printer()
|
|||
update();
|
||||
}
|
||||
|
||||
void PresetComboBox::open_physical_printer_url()
|
||||
{
|
||||
const PhysicalPrinter& pp = m_preset_bundle->physical_printers.get_selected_printer();
|
||||
std::string host = pp.config.opt_string("print_host");
|
||||
assert(!host.empty());
|
||||
wxGetApp().open_browser_with_warning_dialog(host);
|
||||
}
|
||||
|
||||
bool PresetComboBox::del_physical_printer(const wxString& note_string/* = wxEmptyString*/)
|
||||
{
|
||||
const std::string& printer_name = m_preset_bundle->physical_printers.get_selected_full_printer_name();
|
||||
|
@ -752,6 +760,14 @@ void PlaterPresetComboBox::show_edit_menu()
|
|||
append_menu_item(menu, wxID_ANY, _L("Edit physical printer"), "",
|
||||
[this](wxCommandEvent&) { this->edit_physical_printer(); }, "cog", menu, []() { return true; }, wxGetApp().plater());
|
||||
|
||||
const PhysicalPrinter& pp = m_preset_bundle->physical_printers.get_selected_printer();
|
||||
std::string host = pp.config.opt_string("print_host");
|
||||
if (!host.empty()) {
|
||||
append_menu_item(menu, wxID_ANY, _L("Open physical printer URL"), "",
|
||||
[this](wxCommandEvent&) { this->open_physical_printer_url(); }, "open_browser", menu, []() { return true; }, wxGetApp().plater());
|
||||
}
|
||||
|
||||
|
||||
append_menu_item(menu, wxID_ANY, _L("Delete physical printer"), "",
|
||||
[this](wxCommandEvent&) { this->del_physical_printer(); }, "cross", menu, []() { return true; }, wxGetApp().plater());
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
|
||||
void edit_physical_printer();
|
||||
void add_physical_printer();
|
||||
void open_physical_printer_url();
|
||||
bool del_physical_printer(const wxString& note_string = wxEmptyString);
|
||||
void show_modif_preset_separately() { m_show_modif_preset_separately = true; }
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "NotificationManager.hpp"
|
||||
#include "ExtraRenderers.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
|
@ -35,12 +36,14 @@ namespace GUI {
|
|||
|
||||
static const char *CONFIG_KEY_PATH = "printhost_path";
|
||||
static const char *CONFIG_KEY_GROUP = "printhost_group";
|
||||
static const char* CONFIG_KEY_STORAGE = "printhost_storage";
|
||||
|
||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups)
|
||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups, const wxArrayString& storage)
|
||||
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), 0) // Set style = 0 to avoid default creation of the "OK" button.
|
||||
// All buttons will be added later in this constructor
|
||||
, txt_filename(new wxTextCtrl(this, wxID_ANY))
|
||||
, combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr)
|
||||
, combo_storage(storage.GetCount() > 1 ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, storage, wxCB_READONLY) : nullptr)
|
||||
, post_upload_action(PrintHostPostUploadAction::None)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
|
@ -65,6 +68,23 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
|
|||
combo_groups->SetValue(recent_group);
|
||||
}
|
||||
|
||||
if (combo_storage != nullptr) {
|
||||
// PrusaLink specific: User needs to choose a storage
|
||||
auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage:"));
|
||||
content_sizer->Add(label_group);
|
||||
content_sizer->Add(combo_storage, 0, wxBOTTOM, 2 * VERT_SPACING);
|
||||
combo_storage->SetValue(storage.front());
|
||||
wxString recent_storage = from_u8(app_config->get("recent", CONFIG_KEY_STORAGE));
|
||||
if (!recent_storage.empty())
|
||||
combo_storage->SetValue(recent_storage);
|
||||
} else if (storage.GetCount() == 1){
|
||||
// PrusaLink specific: Show which storage has been detected.
|
||||
auto* label_group = new wxStaticText(this, wxID_ANY, _L("Upload to storage: ") + storage.front());
|
||||
content_sizer->Add(label_group);
|
||||
m_preselected_storage = storage.front();
|
||||
}
|
||||
|
||||
|
||||
wxString recent_path = from_u8(app_config->get("recent", CONFIG_KEY_PATH));
|
||||
if (recent_path.Length() > 0 && recent_path[recent_path.Length() - 1] != '/') {
|
||||
recent_path += '/';
|
||||
|
@ -97,6 +117,16 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
|
|||
});
|
||||
txt_filename->SetFocus();
|
||||
|
||||
if (post_actions.has(PrintHostPostUploadAction::QueuePrint)) {
|
||||
auto* btn_print = add_button(wxID_ADD, false, _L("Upload to Queue"));
|
||||
btn_print->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) {
|
||||
if (validate_path(txt_filename->GetValue())) {
|
||||
post_upload_action = PrintHostPostUploadAction::QueuePrint;
|
||||
EndDialog(wxID_OK);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (post_actions.has(PrintHostPostUploadAction::StartPrint)) {
|
||||
auto* btn_print = add_button(wxID_YES, false, _L("Upload and Print"));
|
||||
btn_print->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) {
|
||||
|
@ -162,6 +192,13 @@ std::string PrintHostSendDialog::group() const
|
|||
}
|
||||
}
|
||||
|
||||
std::string PrintHostSendDialog::storage() const
|
||||
{
|
||||
if (!combo_storage)
|
||||
return GUI::format("%1%", m_preselected_storage);
|
||||
return boost::nowide::narrow(combo_storage->GetValue());
|
||||
}
|
||||
|
||||
void PrintHostSendDialog::EndModal(int ret)
|
||||
{
|
||||
if (ret == wxID_OK) {
|
||||
|
@ -180,6 +217,10 @@ void PrintHostSendDialog::EndModal(int ret)
|
|||
wxString group = combo_groups->GetValue();
|
||||
app_config->set("recent", CONFIG_KEY_GROUP, into_u8(group));
|
||||
}
|
||||
if (combo_storage != nullptr) {
|
||||
wxString storage = combo_storage->GetValue();
|
||||
app_config->set("recent", CONFIG_KEY_STORAGE, into_u8(storage));
|
||||
}
|
||||
}
|
||||
|
||||
MsgDialog::EndModal(ret);
|
||||
|
@ -190,7 +231,7 @@ void PrintHostSendDialog::EndModal(int ret)
|
|||
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_RESOLVE, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_INFO, PrintHostQueueDialog::Event);
|
||||
|
||||
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id)
|
||||
: wxEvent(winid, eventType)
|
||||
|
@ -206,7 +247,14 @@ PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_
|
|||
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, wxString error)
|
||||
: wxEvent(winid, eventType)
|
||||
, job_id(job_id)
|
||||
, error(std::move(error))
|
||||
, status(std::move(error))
|
||||
{}
|
||||
|
||||
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, wxString tag, wxString status)
|
||||
: wxEvent(winid, eventType)
|
||||
, job_id(job_id)
|
||||
, tag(std::move(tag))
|
||||
, status(std::move(status))
|
||||
{}
|
||||
|
||||
wxEvent *PrintHostQueueDialog::Event::Clone() const
|
||||
|
@ -219,17 +267,17 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
|||
, on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this)
|
||||
, on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this)
|
||||
, on_cancel_evt(this, EVT_PRINTHOST_CANCEL, &PrintHostQueueDialog::on_cancel, this)
|
||||
, on_resolve_evt(this, EVT_PRINTHOST_RESOLVE, &PrintHostQueueDialog::on_resolve, this)
|
||||
, on_info_evt(this, EVT_PRINTHOST_INFO, &PrintHostQueueDialog::on_info, this)
|
||||
{
|
||||
const auto em = GetTextExtent("m").x;
|
||||
|
||||
auto *topsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
std::vector<int> widths;
|
||||
widths.reserve(6);
|
||||
widths.reserve(7);
|
||||
if (!load_user_data(UDT_COLS, widths)) {
|
||||
widths.clear();
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
for (size_t i = 0; i < 7; i++)
|
||||
widths.push_back(-1);
|
||||
}
|
||||
|
||||
|
@ -252,7 +300,8 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
|||
append_text_column(_L("Host"), widths[3]);
|
||||
append_text_column(_CTX(L_CONTEXT("Size", "OfFile"), "OfFile"), widths[4]);
|
||||
append_text_column(_L("Filename"), widths[5]);
|
||||
append_text_column(_L("Error Message"), -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN);
|
||||
append_text_column(_L("Message"), widths[6]);
|
||||
//append_text_column(_L("Error Message"), -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN);
|
||||
|
||||
auto *btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btn_cancel = new wxButton(this, wxID_DELETE, _L("Cancel selected"));
|
||||
|
@ -423,7 +472,7 @@ void PrintHostQueueDialog::on_error(Event &evt)
|
|||
|
||||
set_state(evt.job_id, ST_ERROR);
|
||||
|
||||
auto errormsg = from_u8((boost::format("%1%\n%2%") % _utf8(L("Error uploading to print host:")) % std::string(evt.error.ToUTF8())).str());
|
||||
auto errormsg = from_u8((boost::format("%1%\n%2%") % _utf8(L("Error uploading to print host:")) % std::string(evt.status.ToUTF8())).str());
|
||||
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
|
||||
job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later
|
||||
|
||||
|
@ -452,15 +501,27 @@ void PrintHostQueueDialog::on_cancel(Event &evt)
|
|||
wxGetApp().notification_manager()->upload_job_notification_show_canceled(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()));
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_resolve(Event& evt)
|
||||
void PrintHostQueueDialog::on_info(Event& evt)
|
||||
{
|
||||
wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list");
|
||||
|
||||
// wxstring in event is called error, but it should contain new host string.
|
||||
wxVariant hst(evt.error);
|
||||
// todo: set variant
|
||||
job_list->SetValue(hst,evt.job_id,COL_HOST);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_host(evt.job_id + 1, boost::nowide::narrow(evt.error));
|
||||
if (evt.tag == L"resolve") {
|
||||
wxVariant hst(evt.status);
|
||||
job_list->SetValue(hst, evt.job_id, COL_HOST);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_host(evt.job_id + 1, boost::nowide::narrow(evt.status));
|
||||
} else if (evt.tag == L"complete") {
|
||||
wxVariant hst(evt.status);
|
||||
job_list->SetValue(hst, evt.job_id, COL_ERRORMSG);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_completed(evt.job_id + 1);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_status(evt.job_id + 1, boost::nowide::narrow(evt.status));
|
||||
} else if(evt.tag == L"complete_with_warning"){
|
||||
wxVariant hst(evt.status);
|
||||
job_list->SetValue(hst, evt.job_id, COL_ERRORMSG);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_completed_with_warning(evt.job_id + 1);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_status(evt.job_id + 1, boost::nowide::narrow(evt.status));
|
||||
} else if (evt.tag == L"set_complete_off") {
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_comp_on_100(evt.job_id + 1, false);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, std::string>>& ret)
|
||||
|
@ -474,7 +535,6 @@ void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, st
|
|||
if(st == JobState::ST_NEW || st == JobState::ST_PROGRESS)
|
||||
ret.emplace_back(upload_names[i]);
|
||||
}
|
||||
//job_list->data
|
||||
}
|
||||
void PrintHostQueueDialog::save_user_data(int udt)
|
||||
{
|
||||
|
@ -526,7 +586,7 @@ bool PrintHostQueueDialog::load_user_data(int udt, std::vector<int>& vector)
|
|||
}
|
||||
if (udt & UserDataType::UDT_COLS)
|
||||
{
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
for (size_t i = 0; i < 7; i++)
|
||||
{
|
||||
if (!hasget("print_host_queue_dialog_column_" + std::to_string(i), vector))
|
||||
return false;
|
||||
|
|
|
@ -26,17 +26,20 @@ namespace GUI {
|
|||
class PrintHostSendDialog : public GUI::MsgDialog
|
||||
{
|
||||
public:
|
||||
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups);
|
||||
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups, const wxArrayString& storage);
|
||||
boost::filesystem::path filename() const;
|
||||
PrintHostPostUploadAction post_action() const;
|
||||
std::string group() const;
|
||||
std::string storage() const;
|
||||
|
||||
virtual void EndModal(int ret) override;
|
||||
private:
|
||||
wxTextCtrl *txt_filename;
|
||||
wxComboBox *combo_groups;
|
||||
wxComboBox* combo_storage;
|
||||
PrintHostPostUploadAction post_upload_action;
|
||||
wxString m_valid_suffix;
|
||||
wxString m_preselected_storage;
|
||||
};
|
||||
|
||||
|
||||
|
@ -48,11 +51,13 @@ public:
|
|||
public:
|
||||
size_t job_id;
|
||||
int progress = 0; // in percent
|
||||
wxString error;
|
||||
wxString tag;
|
||||
wxString status;
|
||||
|
||||
Event(wxEventType eventType, int winid, size_t job_id);
|
||||
Event(wxEventType eventType, int winid, size_t job_id, int progress);
|
||||
Event(wxEventType eventType, int winid, size_t job_id, wxString error);
|
||||
Event(wxEventType eventType, int winid, size_t job_id, wxString tag, wxString status);
|
||||
|
||||
virtual wxEvent *Clone() const;
|
||||
};
|
||||
|
@ -108,7 +113,7 @@ private:
|
|||
EventGuard on_progress_evt;
|
||||
EventGuard on_error_evt;
|
||||
EventGuard on_cancel_evt;
|
||||
EventGuard on_resolve_evt;
|
||||
EventGuard on_info_evt;
|
||||
|
||||
JobState get_state(int idx);
|
||||
void set_state(int idx, JobState);
|
||||
|
@ -116,7 +121,7 @@ private:
|
|||
void on_progress(Event&);
|
||||
void on_error(Event&);
|
||||
void on_cancel(Event&);
|
||||
void on_resolve(Event&);
|
||||
void on_info(Event&);
|
||||
// This vector keep adress and filename of uploads. It is used when checking for running uploads during exit.
|
||||
std::vector<std::pair<std::string, std::string>> upload_names;
|
||||
void save_user_data(int);
|
||||
|
@ -126,7 +131,7 @@ private:
|
|||
wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_RESOLVE, PrintHostQueueDialog::Event);
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_INFO, PrintHostQueueDialog::Event);
|
||||
}}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -892,6 +892,16 @@ void Tab::update_mode()
|
|||
update_changed_tree_ui();
|
||||
}
|
||||
|
||||
void Tab::update_mode_markers()
|
||||
{
|
||||
// update mode for ModeSizer
|
||||
if (m_mode_sizer)
|
||||
m_mode_sizer->update_mode_markers();
|
||||
|
||||
if (m_active_page)
|
||||
m_active_page->refresh();
|
||||
}
|
||||
|
||||
void Tab::update_visibility()
|
||||
{
|
||||
Freeze(); // There is needed Freeze/Thaw to avoid a flashing after Show/Layout
|
||||
|
@ -2638,6 +2648,179 @@ PageShp TabPrinter::build_kinematics_page()
|
|||
return page;
|
||||
}
|
||||
|
||||
const std::vector<std::string> extruder_options = {
|
||||
"min_layer_height", "max_layer_height", "extruder_offset",
|
||||
"retract_length", "retract_lift", "retract_lift_above", "retract_lift_below",
|
||||
"retract_speed", "deretract_speed", "retract_restart_extra", "retract_before_travel",
|
||||
"retract_layer_change", "wipe", "retract_before_wipe",
|
||||
"retract_length_toolchange", "retract_restart_extra_toolchange",
|
||||
};
|
||||
|
||||
void TabPrinter::build_extruder_pages(size_t n_before_extruders)
|
||||
{
|
||||
for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx) {
|
||||
//# build page
|
||||
const wxString&page_name = wxString::Format("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"));
|
||||
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)
|
||||
{
|
||||
if (m_config->opt_bool("single_extruder_multi_material") && m_extruders_count > 1 && opt_key.find_first_of("nozzle_diameter") != std::string::npos)
|
||||
{
|
||||
SuppressBackgroundProcessingUpdate sbpu;
|
||||
const double new_nd = boost::any_cast<double>(value);
|
||||
std::vector<double> nozzle_diameters = static_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values;
|
||||
|
||||
// if value was changed
|
||||
if (fabs(nozzle_diameters[extruder_idx == 0 ? 1 : 0] - new_nd) > EPSILON)
|
||||
{
|
||||
const wxString msg_text = _L("This is a single extruder multimaterial printer, diameters of all extruders "
|
||||
"will be set to the new value. Do you want to proceed?");
|
||||
//wxMessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO);
|
||||
MessageDialog dialog(parent(), msg_text, _L("Nozzle diameter"), wxICON_WARNING | wxYES_NO);
|
||||
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
if (dialog.ShowModal() == wxID_YES) {
|
||||
for (size_t i = 0; i < nozzle_diameters.size(); i++) {
|
||||
if (i==extruder_idx)
|
||||
continue;
|
||||
nozzle_diameters[i] = new_nd;
|
||||
}
|
||||
}
|
||||
else
|
||||
nozzle_diameters[extruder_idx] = nozzle_diameters[extruder_idx == 0 ? 1 : 0];
|
||||
|
||||
new_conf.set_key_value("nozzle_diameter", new ConfigOptionFloats(nozzle_diameters));
|
||||
load_config(new_conf);
|
||||
}
|
||||
}
|
||||
|
||||
update_dirty();
|
||||
update();
|
||||
};
|
||||
|
||||
optgroup = page->new_optgroup(L("Preview"));
|
||||
|
||||
auto reset_to_filament_color = [this, extruder_idx](wxWindow*parent) {
|
||||
ScalableButton* btn = new ScalableButton(parent, wxID_ANY, "undo", _L("Reset to Filament Color"),
|
||||
wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
btn->SetFont(wxGetApp().normal_font());
|
||||
btn->SetSize(btn->GetBestSize());
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [this, extruder_idx](wxCommandEvent&e)
|
||||
{
|
||||
std::vector<std::string> colors = static_cast<const ConfigOptionStrings*>(m_config->option("extruder_colour"))->values;
|
||||
colors[extruder_idx] = "";
|
||||
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("extruder_colour", new ConfigOptionStrings(colors));
|
||||
load_config(new_conf);
|
||||
|
||||
update_dirty();
|
||||
update();
|
||||
});
|
||||
|
||||
parent->Bind(wxEVT_UPDATE_UI, [this, extruder_idx](wxUpdateUIEvent& evt) {
|
||||
evt.Enable(!static_cast<const ConfigOptionStrings*>(m_config->option("extruder_colour"))->values[extruder_idx].empty());
|
||||
}, btn->GetId());
|
||||
|
||||
return sizer;
|
||||
};
|
||||
Line line = optgroup->create_single_option_line("extruder_colour", "", extruder_idx);
|
||||
line.append_widget(reset_to_filament_color);
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup = page->new_optgroup(L(""));
|
||||
|
||||
auto copy_settings_btn =
|
||||
line = { "", ""};
|
||||
line.full_width = 1;
|
||||
line.widget = [this, extruder_idx](wxWindow* parent) {
|
||||
ScalableButton* btn = new ScalableButton(parent, wxID_ANY, "copy", _L("Apply below setting to other extruders"),
|
||||
wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [this, extruder_idx](wxCommandEvent& e) {
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
|
||||
for (const std::string& opt : extruder_options) {
|
||||
const ConfigOption* other_opt = m_config->option(opt);
|
||||
for (size_t extruder = 0; extruder < m_extruders_count; ++extruder) {
|
||||
if (extruder == extruder_idx)
|
||||
continue;
|
||||
static_cast<ConfigOptionVectorBase*>(new_conf.option(opt, false))->set_at(other_opt, extruder, extruder_idx);
|
||||
}
|
||||
}
|
||||
load_config(new_conf);
|
||||
|
||||
update_dirty();
|
||||
update();
|
||||
});
|
||||
|
||||
auto has_changes = [this, extruder_idx]() {
|
||||
auto dirty_options = m_presets->current_dirty_options(true);
|
||||
#if 1
|
||||
dirty_options.erase(std::remove_if(dirty_options.begin(), dirty_options.end(),
|
||||
[](const std::string& opt) { return opt.find("extruder_colour") != std::string::npos || opt.find("nozzle_diameter") != std::string::npos; }), dirty_options.end());
|
||||
return !dirty_options.empty();
|
||||
#else
|
||||
// if we wont to apply enable status for each extruder separately
|
||||
for (const std::string& opt : extruder_options)
|
||||
if (std::find(dirty_options.begin(), dirty_options.end(), opt+"#"+std::to_string(extruder_idx)) != dirty_options.end())
|
||||
return true;
|
||||
return false;
|
||||
#endif
|
||||
};
|
||||
|
||||
parent->Bind(wxEVT_UPDATE_UI, [this, has_changes](wxUpdateUIEvent& evt) {
|
||||
evt.Enable(m_extruders_count > 1 && has_changes());
|
||||
}, btn->GetId());
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
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->append_single_option_line("extruder_offset", "", extruder_idx);
|
||||
|
||||
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 = { 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);
|
||||
|
||||
optgroup->append_single_option_line("retract_speed", "", extruder_idx);
|
||||
optgroup->append_single_option_line("deretract_speed", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_restart_extra", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_before_travel", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_layer_change", "", extruder_idx);
|
||||
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->append_single_option_line("retract_length_toolchange", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_restart_extra_toolchange", "", extruder_idx);
|
||||
}
|
||||
|
||||
// # remove extra pages
|
||||
if (m_extruders_count < m_extruders_count_old)
|
||||
m_pages.erase( m_pages.begin() + n_before_extruders + m_extruders_count,
|
||||
m_pages.begin() + n_before_extruders + m_extruders_count_old);
|
||||
}
|
||||
|
||||
/* Previous name build_extruder_pages().
|
||||
*
|
||||
* This function was renamed because of now it implements not just an extruder pages building,
|
||||
|
@ -2709,114 +2892,7 @@ void TabPrinter::build_unregular_pages(bool from_initial_build/* = false*/)
|
|||
}
|
||||
|
||||
// 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("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"));
|
||||
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)
|
||||
{
|
||||
if (m_config->opt_bool("single_extruder_multi_material") && m_extruders_count > 1 && opt_key.find_first_of("nozzle_diameter") != std::string::npos)
|
||||
{
|
||||
SuppressBackgroundProcessingUpdate sbpu;
|
||||
const double new_nd = boost::any_cast<double>(value);
|
||||
std::vector<double> nozzle_diameters = static_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values;
|
||||
|
||||
// if value was changed
|
||||
if (fabs(nozzle_diameters[extruder_idx == 0 ? 1 : 0] - new_nd) > EPSILON)
|
||||
{
|
||||
const wxString msg_text = _(L("This is a single extruder multimaterial printer, diameters of all extruders "
|
||||
"will be set to the new value. Do you want to proceed?"));
|
||||
//wxMessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO);
|
||||
MessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO);
|
||||
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
if (dialog.ShowModal() == wxID_YES) {
|
||||
for (size_t i = 0; i < nozzle_diameters.size(); i++) {
|
||||
if (i==extruder_idx)
|
||||
continue;
|
||||
nozzle_diameters[i] = new_nd;
|
||||
}
|
||||
}
|
||||
else
|
||||
nozzle_diameters[extruder_idx] = nozzle_diameters[extruder_idx == 0 ? 1 : 0];
|
||||
|
||||
new_conf.set_key_value("nozzle_diameter", new ConfigOptionFloats(nozzle_diameters));
|
||||
load_config(new_conf);
|
||||
}
|
||||
}
|
||||
|
||||
update_dirty();
|
||||
update();
|
||||
};
|
||||
|
||||
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->append_single_option_line("extruder_offset", "", extruder_idx);
|
||||
|
||||
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.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);
|
||||
|
||||
optgroup->append_single_option_line("retract_speed", "", extruder_idx);
|
||||
optgroup->append_single_option_line("deretract_speed", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_restart_extra", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_before_travel", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_layer_change", "", extruder_idx);
|
||||
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->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"));
|
||||
|
||||
auto reset_to_filament_color = [this, extruder_idx](wxWindow* parent) {
|
||||
m_reset_to_filament_color = new ScalableButton(parent, wxID_ANY, "undo", _L("Reset to Filament Color"),
|
||||
wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
ScalableButton* btn = m_reset_to_filament_color;
|
||||
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
btn->SetSize(btn->GetBestSize());
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [this, extruder_idx](wxCommandEvent& e)
|
||||
{
|
||||
std::vector<std::string> colors = static_cast<const ConfigOptionStrings*>(m_config->option("extruder_colour"))->values;
|
||||
colors[extruder_idx] = "";
|
||||
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("extruder_colour", new ConfigOptionStrings(colors));
|
||||
load_config(new_conf);
|
||||
|
||||
update_dirty();
|
||||
update();
|
||||
});
|
||||
|
||||
return sizer;
|
||||
};
|
||||
line = optgroup->create_single_option_line("extruder_colour", "", extruder_idx);
|
||||
line.append_widget(reset_to_filament_color);
|
||||
optgroup->append_line(line);
|
||||
}
|
||||
|
||||
// # remove extra pages
|
||||
if (m_extruders_count < m_extruders_count_old)
|
||||
m_pages.erase( m_pages.begin() + n_before_extruders + m_extruders_count,
|
||||
m_pages.begin() + n_before_extruders + m_extruders_count_old);
|
||||
build_extruder_pages(n_before_extruders);
|
||||
|
||||
Thaw();
|
||||
|
||||
|
@ -2902,7 +2978,6 @@ void TabPrinter::activate_selected_page(std::function<void()> throw_if_canceled)
|
|||
void TabPrinter::clear_pages()
|
||||
{
|
||||
Tab::clear_pages();
|
||||
m_reset_to_filament_color = nullptr;
|
||||
}
|
||||
|
||||
void TabPrinter::toggle_options()
|
||||
|
|
|
@ -355,6 +355,7 @@ public:
|
|||
void load_config(const DynamicPrintConfig& config);
|
||||
virtual void reload_config();
|
||||
void update_mode();
|
||||
void update_mode_markers();
|
||||
void update_visibility();
|
||||
virtual void msw_rescale();
|
||||
virtual void sys_color_changed();
|
||||
|
@ -473,8 +474,6 @@ private:
|
|||
std::vector<PageShp> m_pages_sla;
|
||||
|
||||
public:
|
||||
ScalableButton* m_reset_to_filament_color = nullptr;
|
||||
|
||||
size_t m_extruders_count;
|
||||
size_t m_extruders_count_old = 0;
|
||||
size_t m_initial_extruders_count;
|
||||
|
@ -501,6 +500,7 @@ public:
|
|||
void update_pages(); // update m_pages according to printer technology
|
||||
void extruders_count_changed(size_t extruders_count);
|
||||
PageShp build_kinematics_page();
|
||||
void build_extruder_pages(size_t n_before_extruders);
|
||||
void build_unregular_pages(bool from_initial_build = false);
|
||||
void on_preset_loaded() override;
|
||||
void init_options_list() override;
|
||||
|
|
|
@ -416,7 +416,7 @@ static int scale()
|
|||
}
|
||||
#endif // __WXGTK2__
|
||||
|
||||
wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name_in, int px_cnt/* = 16*/)
|
||||
wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name_in, int px_cnt/* = 16*/, const std::string& new_color/* = std::string()*/)
|
||||
{
|
||||
#ifdef __WXGTK2__
|
||||
px_cnt *= scale();
|
||||
|
@ -428,7 +428,7 @@ wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name_in, int px_cnt/* = 16
|
|||
boost::replace_last(bmp_name, ".png", "");
|
||||
|
||||
// Try loading an SVG first, then PNG if SVG is not found:
|
||||
wxBitmapBundle* bmp = cache.from_svg(bmp_name, px_cnt, px_cnt, Slic3r::GUI::wxGetApp().dark_mode());
|
||||
wxBitmapBundle* bmp = cache.from_svg(bmp_name, px_cnt, px_cnt, Slic3r::GUI::wxGetApp().dark_mode(), new_color);
|
||||
if (bmp == nullptr) {
|
||||
bmp = cache.from_png(bmp_name, px_cnt, px_cnt);
|
||||
if (!bmp)
|
||||
|
@ -655,6 +655,17 @@ ModeButton::ModeButton( wxWindow* parent,
|
|||
Init(mode);
|
||||
}
|
||||
|
||||
ModeButton::ModeButton( wxWindow* parent,
|
||||
int mode_id,/*ConfigOptionMode*/
|
||||
const wxString& mode /*= wxEmptyString*/,
|
||||
int px_cnt /*= = 16*/) :
|
||||
ScalableButton(parent, wxID_ANY, "", mode, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT, px_cnt),
|
||||
m_mode_id(mode_id)
|
||||
{
|
||||
update_bitmap();
|
||||
Init(mode);
|
||||
}
|
||||
|
||||
void ModeButton::Init(const wxString &mode)
|
||||
{
|
||||
std::string mode_str = std::string(mode.ToUTF8());
|
||||
|
@ -684,6 +695,15 @@ void ModeButton::SetState(const bool state)
|
|||
SetToolTip(state ? m_tt_selected : m_tt_focused);
|
||||
}
|
||||
|
||||
void ModeButton::update_bitmap()
|
||||
{
|
||||
m_bmp = *get_bmp_bundle("mode", m_px_cnt, Slic3r::GUI::wxGetApp().get_mode_btn_color(m_mode_id));
|
||||
|
||||
SetBitmap(m_bmp);
|
||||
SetBitmapCurrent(m_bmp);
|
||||
SetBitmapPressed(m_bmp);
|
||||
}
|
||||
|
||||
void ModeButton::focus_button(const bool focus)
|
||||
{
|
||||
const wxFont& new_font = focus ?
|
||||
|
@ -709,6 +729,12 @@ void ModeButton::focus_button(const bool focus)
|
|||
Update();
|
||||
}
|
||||
|
||||
void ModeButton::sys_color_changed()
|
||||
{
|
||||
Slic3r::GUI::wxGetApp().UpdateDarkUI(this, m_has_border);
|
||||
update_bitmap();
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ModeSizer
|
||||
|
@ -721,21 +747,15 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) :
|
|||
{
|
||||
SetFlexibleDirection(wxHORIZONTAL);
|
||||
|
||||
std::vector < std::pair < wxString, std::string >> buttons = {
|
||||
{_(L("Simple")), "mode_simple"},
|
||||
// {_(L("Advanced")), "mode_advanced"},
|
||||
{_CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), "mode_advanced"},
|
||||
{_(L("Expert")), "mode_expert"},
|
||||
};
|
||||
|
||||
auto modebtnfn = [](wxCommandEvent &event, int mode_id) {
|
||||
Slic3r::GUI::wxGetApp().save_mode(mode_id);
|
||||
event.Skip();
|
||||
};
|
||||
|
||||
m_mode_btns.reserve(3);
|
||||
for (const auto& button : buttons) {
|
||||
m_mode_btns.push_back(new ModeButton(parent, button.first, button.second, mode_icon_px_size()));
|
||||
int mode_id = 0;
|
||||
for (const wxString& label : {_L("Simple"), _CTX(L_CONTEXT("Advanced", "Mode"), "Mode"),_L("Expert")}) {
|
||||
m_mode_btns.push_back(new ModeButton(parent, mode_id++, label, mode_icon_px_size()));
|
||||
|
||||
m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, int(m_mode_btns.size() - 1)));
|
||||
Add(m_mode_btns.back());
|
||||
|
@ -762,8 +782,14 @@ void ModeSizer::set_items_border(int border)
|
|||
|
||||
void ModeSizer::sys_color_changed()
|
||||
{
|
||||
for (size_t m = 0; m < m_mode_btns.size(); m++)
|
||||
m_mode_btns[m]->sys_color_changed();
|
||||
for (ModeButton* btn : m_mode_btns)
|
||||
btn->sys_color_changed();
|
||||
}
|
||||
|
||||
void ModeSizer::update_mode_markers()
|
||||
{
|
||||
for (ModeButton* btn : m_mode_btns)
|
||||
btn->update_bitmap();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
@ -50,7 +50,7 @@ void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<
|
|||
int em_unit(wxWindow* win);
|
||||
int mode_icon_px_size();
|
||||
|
||||
wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name, int px_cnt = 16);
|
||||
wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name, int px_cnt = 16, const std::string& new_color_rgb = std::string());
|
||||
wxBitmapBundle* get_empty_bmp_bundle(int width, int height);
|
||||
wxBitmapBundle* get_solid_bmp_bundle(int width, int height, const std::string& color);
|
||||
|
||||
|
@ -247,7 +247,7 @@ public:
|
|||
void SetBitmapDisabled_(const ScalableBitmap &bmp);
|
||||
int GetBitmapHeight();
|
||||
|
||||
void sys_color_changed();
|
||||
virtual void sys_color_changed();
|
||||
|
||||
private:
|
||||
wxWindow* m_parent { nullptr };
|
||||
|
@ -256,6 +256,7 @@ private:
|
|||
int m_width {-1}; // should be multiplied to em_unit
|
||||
int m_height{-1}; // should be multiplied to em_unit
|
||||
|
||||
protected:
|
||||
// bitmap dimensions
|
||||
int m_px_cnt{ 16 };
|
||||
bool m_has_border {false};
|
||||
|
@ -283,6 +284,12 @@ public:
|
|||
const std::string& icon_name = "",
|
||||
int px_cnt = 16);
|
||||
|
||||
ModeButton(
|
||||
wxWindow* parent,
|
||||
int mode_id,/*ConfigOptionMode*/
|
||||
const wxString& mode = wxEmptyString,
|
||||
int px_cnt = 16);
|
||||
|
||||
~ModeButton() {}
|
||||
|
||||
void Init(const wxString& mode);
|
||||
|
@ -292,16 +299,20 @@ public:
|
|||
void OnLeaveBtn(wxMouseEvent& event) { focus_button(m_is_selected); event.Skip(); }
|
||||
|
||||
void SetState(const bool state);
|
||||
void update_bitmap();
|
||||
bool is_selected() { return m_is_selected; }
|
||||
void sys_color_changed() override;
|
||||
|
||||
protected:
|
||||
void focus_button(const bool focus);
|
||||
|
||||
private:
|
||||
bool m_is_selected = false;
|
||||
bool m_is_selected {false};
|
||||
int m_mode_id {-1};
|
||||
|
||||
wxString m_tt_selected;
|
||||
wxString m_tt_focused;
|
||||
wxBitmapBundle m_bmp;
|
||||
};
|
||||
|
||||
|
||||
|
@ -322,6 +333,7 @@ public:
|
|||
void set_items_border(int border);
|
||||
|
||||
void sys_color_changed();
|
||||
void update_mode_markers();
|
||||
const std::vector<ModeButton*>& get_btns() { return m_mode_btns; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -92,7 +92,7 @@ wxString AstroBox::get_test_failed_msg (wxString &msg) const
|
|||
% _utf8(L("Note: AstroBox version at least 1.1.0 is required."))).str());
|
||||
}
|
||||
|
||||
bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg () const override;
|
||||
wxString get_test_failed_msg (wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
bool has_auto_discovery() const override { return true; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
|
|
|
@ -963,10 +963,9 @@ void Bonjour::priv::resolve_perform()
|
|||
};
|
||||
|
||||
std::shared_ptr< boost::asio::io_service > io_service(new boost::asio::io_service);
|
||||
|
||||
std::vector<ResolveSocket*> sockets;
|
||||
|
||||
// resolve intefaces - from PR#6646
|
||||
|
||||
// resolve interfaces - from PR#6646
|
||||
std::vector<boost::asio::ip::address> interfaces;
|
||||
asio::ip::udp::resolver resolver(*io_service);
|
||||
boost::system::error_code ec;
|
||||
|
|
|
@ -54,7 +54,7 @@ wxString Duet::get_test_failed_msg (wxString &msg) const
|
|||
% std::string(msg.ToUTF8())).str());
|
||||
}
|
||||
|
||||
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
wxString connect_msg;
|
||||
auto connectionType = connect(connect_msg);
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint | PrintHostPostUploadAction::StartSimulation; }
|
||||
|
|
|
@ -76,7 +76,7 @@ wxString FlashAir::get_test_failed_msg (wxString &msg) const
|
|||
% _utf8(L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required."))).str());
|
||||
}
|
||||
|
||||
bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
|
||||
|
|
|
@ -62,7 +62,7 @@ wxString MKS::get_test_failed_msg(wxString& msg) const
|
|||
% std::string(msg.ToUTF8())).str());
|
||||
}
|
||||
|
||||
bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
bool test(wxString& curl_msg) const override;
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString& msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
|
@ -16,7 +18,8 @@
|
|||
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "Http.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "Bonjour.hpp"
|
||||
|
@ -28,9 +31,9 @@ namespace pt = boost::property_tree;
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
#ifdef WIN32
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
namespace {
|
||||
#ifdef WIN32
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
|
||||
{
|
||||
// put ipv6 into [] brackets
|
||||
|
@ -125,8 +128,34 @@ std::string get_host_from_url(const std::string& url_in)
|
|||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url";
|
||||
return out;
|
||||
}
|
||||
} //namespace
|
||||
#endif // WIN32
|
||||
std::string escape_string(const std::string& unescaped)
|
||||
{
|
||||
std::string ret_val;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
char* decoded = curl_easy_escape(curl, unescaped.c_str(), unescaped.size());
|
||||
if (decoded) {
|
||||
ret_val = std::string(decoded);
|
||||
curl_free(decoded);
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
std::string escape_path_by_element(const boost::filesystem::path& path)
|
||||
{
|
||||
std::string ret_val = escape_string(path.filename().string());
|
||||
boost::filesystem::path parent(path.parent_path());
|
||||
while (!parent.empty() && parent.string() != "/") // "/" check is for case "/file.gcode" was inserted. Then boost takes "/" as parent_path.
|
||||
{
|
||||
ret_val = escape_string(parent.filename().string()) + "/" + ret_val;
|
||||
parent = parent.parent_path();
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
} //namespace
|
||||
|
||||
|
||||
OctoPrint::OctoPrint(DynamicPrintConfig *config) :
|
||||
m_host(config->opt_string("print_host")),
|
||||
|
@ -258,10 +287,10 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const
|
|||
% _utf8(L("Note: OctoPrint version at least 1.1.0 is required."))).str());
|
||||
}
|
||||
|
||||
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
#ifndef WIN32
|
||||
return upload_inner_with_host(upload_data, prorgess_fn, error_fn, resolve_fn);
|
||||
return upload_inner_with_host(std::move(upload_data), prorgess_fn, error_fn, info_fn);
|
||||
#else
|
||||
std::string host = get_host_from_url(m_host);
|
||||
|
||||
|
@ -271,13 +300,12 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
|||
boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec);
|
||||
if (!ec) {
|
||||
resolved_addr.push_back(host_ip);
|
||||
} else if ( GUI::get_app_config()->get("allow_ip_resolve") == "1"){
|
||||
} else if ( GUI::get_app_config()->get("allow_ip_resolve") == "1" && boost::algorithm::ends_with(host, ".local")){
|
||||
Bonjour("octoprint")
|
||||
.set_hostname(host)
|
||||
.set_retries(10) // number of rounds of queries send
|
||||
.set_retries(5) // number of rounds of queries send
|
||||
.set_timeout(1) // after each timeout, if there is any answer, the resolving will stop
|
||||
.on_resolve([&ra = resolved_addr](const std::vector<BonjourReply>& replies) {
|
||||
std::vector<boost::asio::ip::address> resolved_addr;
|
||||
for (const auto & rpl : replies) {
|
||||
boost::asio::ip::address ip(rpl.ip);
|
||||
ra.emplace_back(ip);
|
||||
|
@ -289,21 +317,21 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
|||
if (resolved_addr.empty()) {
|
||||
// no resolved addresses - try system resolving
|
||||
BOOST_LOG_TRIVIAL(error) << "PrusaSlicer failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving.";
|
||||
return upload_inner_with_host(upload_data, prorgess_fn, error_fn, resolve_fn);
|
||||
return upload_inner_with_host(std::move(upload_data), prorgess_fn, error_fn, info_fn);
|
||||
} else if (resolved_addr.size() == 1) {
|
||||
// one address resolved - upload there
|
||||
return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr.front());
|
||||
return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr.front());
|
||||
} else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) {
|
||||
// there are just 2 addresses and 1 is ip_v4 and other is ip_v6
|
||||
// try sending to both. (Then if both fail, show both error msg after second try)
|
||||
wxString error_message;
|
||||
if (!upload_inner_with_resolved_ip(upload_data, prorgess_fn
|
||||
if (!upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn
|
||||
, [&msg = error_message, resolved_addr](wxString error) { msg = GUI::format_wxstr("%1%: %2%", resolved_addr.front(), error); }
|
||||
, resolve_fn, resolved_addr.front())
|
||||
, info_fn, resolved_addr.front())
|
||||
&&
|
||||
!upload_inner_with_resolved_ip(upload_data, prorgess_fn
|
||||
!upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn
|
||||
, [&msg = error_message, resolved_addr](wxString error) { msg += GUI::format_wxstr("\n%1%: %2%", resolved_addr.back(), error); }
|
||||
, resolve_fn, resolved_addr.back())
|
||||
, info_fn, resolved_addr.back())
|
||||
) {
|
||||
|
||||
error_fn(error_message);
|
||||
|
@ -315,16 +343,16 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
|||
size_t selected_index = resolved_addr.size();
|
||||
IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index);
|
||||
if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) {
|
||||
return upload_inner_with_resolved_ip(upload_data, prorgess_fn, error_fn, resolve_fn, resolved_addr[selected_index]);
|
||||
return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr[selected_index]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#endif // WIN32
|
||||
}
|
||||
#ifdef WIN32
|
||||
bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr) const
|
||||
bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const
|
||||
{
|
||||
resolve_fn(boost::nowide::widen(resolved_addr.to_string()));
|
||||
info_fn(L"resolve", boost::nowide::widen(resolved_addr.to_string()));
|
||||
|
||||
// If test fails, test_msg_or_host_ip contains the error message.
|
||||
// Otherwise on Windows it contains the resolved IP address of the host.
|
||||
|
@ -341,7 +369,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
|
|||
std::string url = substitute_host(make_url("api/files/local"), resolved_addr.to_string());
|
||||
bool result = true;
|
||||
|
||||
resolve_fn(boost::nowide::widen(url));
|
||||
info_fn(L"resolve", boost::nowide::widen(url));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%")
|
||||
% name
|
||||
|
@ -356,6 +384,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
|
|||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
|
||||
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
|
||||
})
|
||||
|
@ -379,7 +408,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
|
|||
}
|
||||
#endif //WIN32
|
||||
|
||||
bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
const char* name = get_name();
|
||||
|
||||
|
@ -414,7 +443,7 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
|
|||
// This new address returns in "test_msg_or_host_ip" variable.
|
||||
// Solves troubles of uploades failing with name address.
|
||||
// in original address (m_host) replace host for resolved ip
|
||||
resolve_fn(test_msg_or_host_ip);
|
||||
info_fn(L"resolve", test_msg_or_host_ip);
|
||||
url = substitute_host(make_url("api/files/local"), GUI::into_u8(test_msg_or_host_ip));
|
||||
BOOST_LOG_TRIVIAL(info) << "Upload address after ip resolve: " << url;
|
||||
}
|
||||
|
@ -529,11 +558,12 @@ void SL1Host::set_auth(Http &http) const
|
|||
}
|
||||
|
||||
// PrusaLink
|
||||
PrusaLink::PrusaLink(DynamicPrintConfig* config) :
|
||||
PrusaLink::PrusaLink(DynamicPrintConfig* config, bool show_after_message) :
|
||||
OctoPrint(config),
|
||||
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
|
||||
m_username(config->opt_string("printhost_user")),
|
||||
m_password(config->opt_string("printhost_password"))
|
||||
m_password(config->opt_string("printhost_password")),
|
||||
m_show_after_message(show_after_message)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -572,4 +602,527 @@ void PrusaLink::set_auth(Http& http) const
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool PrusaLink::version_check(const boost::optional<std::string>& version_text) const
|
||||
{
|
||||
// version_text is in format OctoPrint 1.2.3
|
||||
// true (= use PUT) should return:
|
||||
// PrusaLink 0.7+
|
||||
|
||||
try {
|
||||
if (!version_text)
|
||||
throw Slic3r::RuntimeError("no version_text was given");
|
||||
|
||||
std::vector<std::string> name_and_version;
|
||||
boost::algorithm::split(name_and_version, *version_text, boost::is_any_of(" "));
|
||||
|
||||
if (name_and_version.size() != 2)
|
||||
throw Slic3r::RuntimeError("invalid version_text");
|
||||
|
||||
Semver semver(name_and_version[1]); // throws Slic3r::RuntimeError when unable to parse
|
||||
if (name_and_version.front() == "PrusaLink" && semver >= Semver(0, 7, 0))
|
||||
return true;
|
||||
} catch (const Slic3r::RuntimeError& ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << std::string("Print host version check failed: ") + ex.what();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool PrusaLink::test(wxString& msg) const
|
||||
{
|
||||
// Since the request is performed synchronously here,
|
||||
// it is ok to refer to `msg` from within the closure
|
||||
const char* name = get_name();
|
||||
|
||||
bool res = true;
|
||||
auto url = make_url("api/version");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
|
||||
|
||||
try {
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
pt::read_json(ss, ptree);
|
||||
|
||||
if (!ptree.get_optional<std::string>("api")) {
|
||||
res = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto text = ptree.get_optional<std::string>("text");
|
||||
res = validate_version_text(text);
|
||||
if (!res) {
|
||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
||||
}
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
res = false;
|
||||
msg = "Could not parse server response";
|
||||
}
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.on_ip_resolve([&](std::string address) {
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
// Remember resolved address to be reused at successive REST API call.
|
||||
msg = GUI::from_u8(address);
|
||||
})
|
||||
#endif // WIN32
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool PrusaLink::get_storage(wxArrayString& output) const
|
||||
{
|
||||
const char* name = get_name();
|
||||
|
||||
bool res = true;
|
||||
auto url = make_url("api/v1/storage");
|
||||
wxString error_msg;
|
||||
|
||||
struct StorageInfo{
|
||||
wxString name;
|
||||
bool read_only;
|
||||
long long free_space;
|
||||
};
|
||||
std::vector<StorageInfo> storage;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get storage at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting storage: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
error_msg = L"\n\n" + boost::nowide::widen(error);
|
||||
res = false;
|
||||
// If status is 0, the communication with the printer has failed completely (most likely a timeout), if the status is <= 400, it is an error returned by the pritner.
|
||||
// If 0, we can show error to the user now, as we know the communication has failed. (res = true will do the trick.)
|
||||
// if not 0, we must not show error, as not all printers support api/v1/storage endpoint.
|
||||
// So we must be extra careful here, or we might be showing errors on perfectly fine communication.
|
||||
if (status == 0)
|
||||
res = true;
|
||||
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got storage: %2%") % name % body;
|
||||
try
|
||||
{
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
pt::read_json(ss, ptree);
|
||||
|
||||
// what if there is more structure added in the future? Enumerate all elements?
|
||||
if (ptree.front().first != "storage_list") {
|
||||
res = false;
|
||||
return;
|
||||
}
|
||||
// each storage has own subtree of storage_list
|
||||
for (const auto& section : ptree.front().second) {
|
||||
const auto path = section.second.get_optional<std::string>("path");
|
||||
const auto space = section.second.get_optional<std::string>("free_space");
|
||||
const auto read_only = section.second.get_optional<bool>("read_only");
|
||||
const auto available = section.second.get_optional<bool>("available");
|
||||
if (path && (!available || *available)) {
|
||||
StorageInfo si;
|
||||
si.name = boost::nowide::widen(*path);
|
||||
si.read_only = read_only ? *read_only : false; // If read_only is missing, assume it is NOT read only.
|
||||
si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space.
|
||||
storage.emplace_back(std::move(si));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
|
||||
#endif // WIN32
|
||||
.perform_sync();
|
||||
|
||||
for (const auto& si : storage) {
|
||||
if (!si.read_only && si.free_space > 0)
|
||||
output.push_back(si.name);
|
||||
}
|
||||
|
||||
if (res && output.empty())
|
||||
{
|
||||
if (!storage.empty()) { // otherwise error_msg is already filled
|
||||
error_msg = L"\n\n" + _L("Storages found:") + L" \n";
|
||||
for (const auto& si : storage) {
|
||||
error_msg += si.name + L" : " + (si.read_only ? _L("read only") : _L("no free space")) + L"\n";
|
||||
}
|
||||
}
|
||||
std::string message = GUI::format(_L("Upload has failed. There is no suitable storage found at %1%.%2%"), m_host, error_msg);
|
||||
BOOST_LOG_TRIVIAL(error) << message;
|
||||
throw Slic3r::IOError(message);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool PrusaLink::test_with_method_check(wxString& msg, bool& use_put) const
|
||||
{
|
||||
// Since the request is performed synchronously here,
|
||||
// it is ok to refer to `msg` from within the closure
|
||||
|
||||
const char* name = get_name();
|
||||
|
||||
bool res = true;
|
||||
auto url = make_url("api/version");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
|
||||
|
||||
try {
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
pt::read_json(ss, ptree);
|
||||
|
||||
if (!ptree.get_optional<std::string>("api")) {
|
||||
res = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto text = ptree.get_optional<std::string>("text");
|
||||
res = validate_version_text(text);
|
||||
if (!res) {
|
||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
||||
use_put = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// find capabilities subtree and read upload-by-put
|
||||
for (const auto& section : ptree) {
|
||||
if (section.first == "capabilities") {
|
||||
const auto put_upload = section.second.get_optional<bool>("upload-by-put");
|
||||
if (put_upload)
|
||||
use_put = *put_upload;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
res = false;
|
||||
msg = "Could not parse server response";
|
||||
}
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.on_ip_resolve([&](std::string address) {
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
// Remember resolved address to be reused at successive REST API call.
|
||||
msg = GUI::from_u8(address);
|
||||
})
|
||||
#endif // WIN32
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
bool PrusaLink::test_with_resolved_ip_and_method_check(wxString& msg, bool& use_put) const
|
||||
{
|
||||
// Since the request is performed synchronously here,
|
||||
// it is ok to refer to `msg` from within the closure
|
||||
const char* name = get_name();
|
||||
bool res = true;
|
||||
// Msg contains ip string.
|
||||
auto url = substitute_host(make_url("api/version"), GUI::into_u8(msg));
|
||||
msg.Clear();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(url);//std::move(url));
|
||||
set_auth(http);
|
||||
http
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version at %2% : %3%, HTTP %4%, body: `%5%`") % name % url % error % status % body;
|
||||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Got version: %2%") % name % body;
|
||||
|
||||
try {
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
pt::read_json(ss, ptree);
|
||||
|
||||
if (!ptree.get_optional<std::string>("api")) {
|
||||
res = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto text = ptree.get_optional<std::string>("text");
|
||||
res = validate_version_text(text);
|
||||
if (!res) {
|
||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
|
||||
use_put = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// find capabilities subtree and read upload-by-put
|
||||
for (const auto& section : ptree) {
|
||||
if (section.first == "capabilities") {
|
||||
const auto put_upload = section.second.get_optional<bool>("upload-by-put");
|
||||
if (put_upload)
|
||||
use_put = *put_upload;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
res = false;
|
||||
msg = "Could not parse server response";
|
||||
}
|
||||
|
||||
})
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool PrusaLink::upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const
|
||||
{
|
||||
info_fn(L"resolve", boost::nowide::widen(resolved_addr.to_string()));
|
||||
|
||||
// If test fails, test_msg_or_host_ip contains the error message.
|
||||
// Otherwise on Windows it contains the resolved IP address of the host.
|
||||
// Test_msg already contains resolved ip and will be cleared on start of test().
|
||||
wxString test_msg_or_host_ip = GUI::from_u8(resolved_addr.to_string());
|
||||
bool use_put = false;
|
||||
if (!test_with_resolved_ip_and_method_check(test_msg_or_host_ip, use_put)) {
|
||||
error_fn(std::move(test_msg_or_host_ip));
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* name = get_name();
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
std::string storage_path = (use_put ? "api/v1/files" : "api/files");
|
||||
storage_path += (upload_data.storage.empty() ? "/local" : upload_data.storage);
|
||||
std::string url = substitute_host(make_url(storage_path), resolved_addr.to_string());
|
||||
bool result = true;
|
||||
info_fn(L"resolve", boost::nowide::widen(url));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, method: %7%")
|
||||
% name
|
||||
% upload_data.source_path
|
||||
% url
|
||||
% upload_filename.string()
|
||||
% upload_parent_path.string()
|
||||
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
% (use_put ? "PUT" : "POST");
|
||||
|
||||
if (use_put)
|
||||
return put_inner(std::move(upload_data), std::move(url), name, prorgess_fn, error_fn, info_fn);
|
||||
return post_inner(std::move(upload_data), std::move(url), name, prorgess_fn, error_fn, info_fn);
|
||||
}
|
||||
|
||||
#endif //WIN32
|
||||
|
||||
bool PrusaLink::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
const char* name = get_name();
|
||||
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
|
||||
// If test fails, test_msg_or_host_ip contains the error message.
|
||||
// Otherwise on Windows it contains the resolved IP address of the host.
|
||||
wxString test_msg_or_host_ip;
|
||||
bool use_put = false;
|
||||
if (!test_with_method_check(test_msg_or_host_ip, use_put)) {
|
||||
error_fn(std::move(test_msg_or_host_ip));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string url;
|
||||
std::string storage_path = (use_put ? "api/v1/files" : "api/files");
|
||||
storage_path += (upload_data.storage.empty() ? "/local" : upload_data.storage);
|
||||
#ifdef WIN32
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || GUI::get_app_config()->get("allow_ip_resolve") != "1")
|
||||
#endif // _WIN32
|
||||
{
|
||||
// If https is entered we assume signed ceritificate is being used
|
||||
// IP resolving will not happen - it could resolve into address not being specified in cert
|
||||
url = make_url(storage_path);
|
||||
}
|
||||
#ifdef WIN32
|
||||
else {
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
// Curl uses easy_getinfo to get ip address of last successful transaction.
|
||||
// If it got the address use it instead of the stored in "host" variable.
|
||||
// This new address returns in "test_msg_or_host_ip" variable.
|
||||
// Solves troubles of uploades failing with name address.
|
||||
// in original address (m_host) replace host for resolved ip
|
||||
info_fn(L"resolve", test_msg_or_host_ip);
|
||||
url = substitute_host(make_url(storage_path), GUI::into_u8(test_msg_or_host_ip));
|
||||
BOOST_LOG_TRIVIAL(info) << "Upload address after ip resolve: " << url;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, method: %7%")
|
||||
% name
|
||||
% upload_data.source_path
|
||||
% url
|
||||
% upload_filename.string()
|
||||
% upload_parent_path.string()
|
||||
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
% (use_put ? "PUT" : "POST");
|
||||
|
||||
if (use_put)
|
||||
return put_inner(std::move(upload_data), std::move(url), name, prorgess_fn, error_fn, info_fn);
|
||||
return post_inner(std::move(upload_data), std::move(url), name, prorgess_fn, error_fn, info_fn);
|
||||
}
|
||||
|
||||
bool PrusaLink::put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
info_fn(L"set_complete_off", wxString());
|
||||
|
||||
bool res = true;
|
||||
// Percent escape all filenames in on path and add it to the url. This is different from POST.
|
||||
url += "/" + escape_path_by_element(upload_data.upload_path);
|
||||
|
||||
Http http = Http::put(std::move(url));
|
||||
set_auth(http);
|
||||
// This is ugly, but works. There was an error at PrusaLink side that accepts any string at Print-After-Upload as true, thus False was also triggering print after upload.
|
||||
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint)
|
||||
http.header("Print-After-Upload", "True");
|
||||
|
||||
http.set_put_body(upload_data.source_path)
|
||||
.header("Content-Type", "text/x.gcode")
|
||||
.header("Overwrite", "?1")
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
wxString widebody = wxString::FromUTF8(body);
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % widebody;
|
||||
std::string message = m_show_after_message ? (boost::format("%1%") % widebody).str() : std::string();
|
||||
info_fn(L"complete", message);
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
error_fn(format_error(body, error, status));
|
||||
res = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool& cancel) {
|
||||
prorgess_fn(std::move(progress), cancel);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
#endif
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
bool PrusaLink::post_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
info_fn(L"set_complete_off", wxString());
|
||||
bool res = true;
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
Http http = Http::post(std::move(url));
|
||||
set_auth(http);
|
||||
set_http_post_header_args(http, upload_data.post_action);
|
||||
http.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
if (m_show_after_message) {
|
||||
// PrusaConnect message
|
||||
wxString widebody = wxString::FromUTF8(body);
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % widebody;
|
||||
std::string message = m_show_after_message ? (boost::format("%1%") % widebody).str() : std::string();
|
||||
if (status == 202)
|
||||
info_fn(L"complete_with_warning", message);
|
||||
else
|
||||
info_fn(L"complete", message);
|
||||
} else {
|
||||
// PrusaLink
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%") % name % status;
|
||||
info_fn(L"complete", wxString());
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
error_fn(format_error(body, error, status));
|
||||
res = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool& cancel) {
|
||||
prorgess_fn(std::move(progress), cancel);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
#endif
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void PrusaLink::set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const
|
||||
{
|
||||
http.form_add("print", post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
|
||||
}
|
||||
|
||||
PrusaConnect::PrusaConnect(DynamicPrintConfig* config)
|
||||
: PrusaLink(config, true)
|
||||
{
|
||||
}
|
||||
|
||||
void PrusaConnect::set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const
|
||||
{
|
||||
// Language for accept message
|
||||
wxString wlang = GUI::wxGetApp().current_language_code();
|
||||
std::string lang = GUI::format(wlang.SubString(0, 1));
|
||||
http.header("Accept-Language", lang);
|
||||
// Post action
|
||||
if (post_action == PrintHostPostUploadAction::StartPrint) {
|
||||
http.form_add("to_print", "True");
|
||||
} else if (post_action == PrintHostPostUploadAction::QueuePrint) {
|
||||
http.form_add("to_queue", "True");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,10 +23,10 @@ public:
|
|||
|
||||
const char* get_name() const override;
|
||||
|
||||
bool test(wxString &curl_msg) const override;
|
||||
virtual bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg () const override;
|
||||
wxString get_test_failed_msg (wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
bool has_auto_discovery() const override { return true; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
|
@ -35,14 +35,12 @@ public:
|
|||
const std::string& get_cafile() const { return m_cafile; }
|
||||
|
||||
protected:
|
||||
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
|
||||
#ifdef WIN32
|
||||
virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn, const boost::asio::ip::address& resolved_addr) const;
|
||||
virtual bool test_with_resolved_ip(wxString& curl_msg) const;
|
||||
virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const;
|
||||
#endif
|
||||
virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const;
|
||||
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
|
||||
virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const;
|
||||
|
||||
private:
|
||||
std::string m_host;
|
||||
std::string m_apikey;
|
||||
std::string m_cafile;
|
||||
|
@ -50,6 +48,11 @@ private:
|
|||
|
||||
virtual void set_auth(Http &http) const;
|
||||
std::string make_url(const std::string &path) const;
|
||||
|
||||
private:
|
||||
#ifdef WIN32
|
||||
bool test_with_resolved_ip(wxString& curl_msg) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
class SL1Host: public OctoPrint
|
||||
|
@ -80,26 +83,57 @@ private:
|
|||
class PrusaLink : public OctoPrint
|
||||
{
|
||||
public:
|
||||
PrusaLink(DynamicPrintConfig* config);
|
||||
PrusaLink(DynamicPrintConfig* config) : PrusaLink(config, false) {}
|
||||
PrusaLink(DynamicPrintConfig* config, bool show_after_message);
|
||||
~PrusaLink() override = default;
|
||||
|
||||
const char* get_name() const override;
|
||||
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString& msg) const override;
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
virtual PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
|
||||
// gets possible storage to be uploaded to. This allows different printer to have different storage. F.e. local vs sdcard vs usb.
|
||||
bool get_storage(wxArrayString& /* storage */) const override;
|
||||
protected:
|
||||
bool test(wxString& curl_msg) const override;
|
||||
bool validate_version_text(const boost::optional<std::string>& version_text) const override;
|
||||
bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
|
||||
void set_auth(Http& http) const override;
|
||||
virtual void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const;
|
||||
#ifdef WIN32
|
||||
bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void set_auth(Http& http) const override;
|
||||
|
||||
bool test_with_method_check(wxString& curl_msg, bool& use_put) const;
|
||||
bool put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const;
|
||||
bool post_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const;
|
||||
#ifdef WIN32
|
||||
bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const;
|
||||
#endif
|
||||
// Host authorization type.
|
||||
AuthorizationType m_authorization_type;
|
||||
// username and password for HTTP Digest Authentization (RFC RFC2617)
|
||||
std::string m_username;
|
||||
std::string m_password;
|
||||
bool m_show_after_message;
|
||||
|
||||
#if 0
|
||||
bool version_check(const boost::optional<std::string>& version_text) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
class PrusaConnect : public PrusaLink
|
||||
{
|
||||
public:
|
||||
PrusaConnect(DynamicPrintConfig* config);
|
||||
~PrusaConnect() override = default;
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint | PrintHostPostUploadAction::QueuePrint; }
|
||||
const char* get_name() const override { return "PrusaConnect"; }
|
||||
protected:
|
||||
void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
|||
case htAstroBox: return new AstroBox(config);
|
||||
case htRepetier: return new Repetier(config);
|
||||
case htPrusaLink: return new PrusaLink(config);
|
||||
case htPrusaConnect: return new PrusaConnect(config);
|
||||
case htMKS: return new MKS(config);
|
||||
default: return nullptr;
|
||||
}
|
||||
|
@ -93,12 +94,13 @@ struct PrintHostJobQueue::priv
|
|||
void emit_progress(int progress);
|
||||
void emit_error(wxString error);
|
||||
void emit_cancel(size_t id);
|
||||
void emit_resolve(wxString host);
|
||||
void emit_info(wxString tag, wxString status);
|
||||
void start_bg_thread();
|
||||
void stop_bg_thread();
|
||||
void bg_thread_main();
|
||||
void progress_fn(Http::Progress progress, bool &cancel);
|
||||
void error_fn(wxString error);
|
||||
void info_fn(wxString tag, wxString status);
|
||||
void remove_source(const fs::path &path);
|
||||
void remove_source();
|
||||
void perform_job(PrintHostJob the_job);
|
||||
|
@ -127,19 +129,18 @@ void PrintHostJobQueue::priv::emit_error(wxString error)
|
|||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::emit_info(wxString tag, wxString status)
|
||||
{
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_INFO, queue_dialog->GetId(), job_id, std::move(tag), std::move(status));
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::emit_cancel(size_t id)
|
||||
{
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, queue_dialog->GetId(), id);
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::emit_resolve(wxString host)
|
||||
{
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_RESOLVE, queue_dialog->GetId(), job_id, host);
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
|
||||
void PrintHostJobQueue::priv::start_bg_thread()
|
||||
{
|
||||
if (bg_thread.joinable()) { return; }
|
||||
|
@ -271,6 +272,10 @@ void PrintHostJobQueue::priv::error_fn(wxString error)
|
|||
emit_error(std::move(error));
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::info_fn(wxString tag, wxString status)
|
||||
{
|
||||
emit_info(tag, status);
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::remove_source(const fs::path &path)
|
||||
{
|
||||
|
@ -296,7 +301,7 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
|||
bool success = the_job.printhost->upload(std::move(the_job.upload_data),
|
||||
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||
[this](wxString error) { this->error_fn(std::move(error)); },
|
||||
[this](wxString host) { emit_resolve(std::move(host)); }
|
||||
[this](wxString tag, wxString host) { this->info_fn(std::move(tag), std::move(host)); }
|
||||
);
|
||||
|
||||
if (success) {
|
||||
|
|
|
@ -21,7 +21,8 @@ class DynamicPrintConfig;
|
|||
enum class PrintHostPostUploadAction {
|
||||
None,
|
||||
StartPrint,
|
||||
StartSimulation
|
||||
StartSimulation,
|
||||
QueuePrint
|
||||
};
|
||||
using PrintHostPostUploadActions = enum_bitmask<PrintHostPostUploadAction>;
|
||||
ENABLE_ENUM_BITMASK_OPERATORS(PrintHostPostUploadAction);
|
||||
|
@ -32,7 +33,8 @@ struct PrintHostUpload
|
|||
boost::filesystem::path upload_path;
|
||||
|
||||
std::string group;
|
||||
|
||||
std::string storage;
|
||||
|
||||
PrintHostPostUploadAction post_action { PrintHostPostUploadAction::None };
|
||||
};
|
||||
|
||||
|
@ -43,14 +45,14 @@ public:
|
|||
|
||||
typedef Http::ProgressFn ProgressFn;
|
||||
typedef std::function<void(wxString /* error */)> ErrorFn;
|
||||
typedef ErrorFn ResolveFn;
|
||||
typedef std::function<void(wxString /* tag */, wxString /* status */)> InfoFn;
|
||||
|
||||
virtual const char* get_name() const = 0;
|
||||
|
||||
virtual bool test(wxString &curl_msg) const = 0;
|
||||
virtual wxString get_test_ok_msg () const = 0;
|
||||
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
||||
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const = 0;
|
||||
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const = 0;
|
||||
virtual bool has_auto_discovery() const = 0;
|
||||
virtual bool can_test() const = 0;
|
||||
virtual PrintHostPostUploadActions get_post_upload_actions() const = 0;
|
||||
|
@ -62,6 +64,9 @@ public:
|
|||
// Returns false if not supported. May throw HostNetworkError.
|
||||
virtual bool get_groups(wxArrayString & /* groups */) const { return false; }
|
||||
virtual bool get_printers(wxArrayString & /* printers */) const { return false; }
|
||||
// Support for PrusaLink uploading to different storage. Not supported by other print hosts.
|
||||
// Returns false if not supported or fail.
|
||||
virtual bool get_storage(wxArrayString& /* storage */) const { return false; }
|
||||
|
||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ bool Repetier::test(wxString &msg) const
|
|||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
.on_complete([&](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
|
||||
|
||||
try {
|
||||
|
@ -111,7 +111,7 @@ wxString Repetier::get_test_failed_msg (wxString &msg) const
|
|||
% _utf8(L("Note: Repetier version at least 0.90.0 is required."))).str());
|
||||
}
|
||||
|
||||
bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const
|
||||
bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg () const override;
|
||||
wxString get_test_failed_msg (wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, ResolveFn resolve_fn) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||
|
|
Loading…
Add table
Reference in a new issue