mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-06 07:11:10 +00:00
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes. clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer. * Remove Unused Project Includes From Files With Platform-Specific Code A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block. * Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass The platform-file pass treated pchheader.hpp as an ordinary header and emptied it, and left GUI_Preview.hpp and 14 other files relying on headers they no longer reached directly. * Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call * Include Headers That Files Reached Through Ones the Cleanup Removed * Drop Includes Duplicated by the Cleanup or by Main's Own Additions * Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
916 lines
36 KiB
C++
916 lines
36 KiB
C++
#include "MsgDialog.hpp"
|
|
|
|
#include "slic3r/GUI/GUI_Utils.hpp"
|
|
#include <wx/event.h>
|
|
#include <wx/gdicmn.h>
|
|
#include <wx/dialog.h>
|
|
#include <wx/font.h>
|
|
#include "slic3r/GUI/Widgets/CheckBox.hpp"
|
|
#include "slic3r/GUI/ReleaseNote.hpp"
|
|
#include "slic3r/GUI/Widgets/StateColor.hpp"
|
|
#include "slic3r/GUI/Widgets/Button.hpp"
|
|
#include <string>
|
|
#include <vector>
|
|
#include <utility>
|
|
#include <cstddef>
|
|
#include <wx/chartype.h>
|
|
#include <wx/html/htmltag.h>
|
|
#include <wx/dcclient.h>
|
|
#include <functional>
|
|
#include <cmath>
|
|
#include <cassert>
|
|
#include <wx/panel.h>
|
|
#include "libslic3r/Semver.hpp"
|
|
#include "slic3r/GUI/Widgets/HyperLink.hpp"
|
|
#include <boost/log/trivial.hpp>
|
|
#include <wx/settings.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/stattext.h>
|
|
#include <wx/button.h>
|
|
#include <wx/statbmp.h>
|
|
#include <wx/scrolwin.h>
|
|
#include <wx/clipbrd.h>
|
|
#include <wx/checkbox.h>
|
|
#include <wx/html/htmlwin.h>
|
|
#include <wx/html/winpars.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
#include <boost/algorithm/string/split.hpp>
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
#include <wx/string.h>
|
|
#include <wx/tglbtn.h>
|
|
#include <wx/toplevel.h>
|
|
#include <wx/utils.h>
|
|
|
|
#include "libslic3r/libslic3r.h"
|
|
#include "libslic3r/Utils.hpp"
|
|
#include "libslic3r/Color.hpp"
|
|
#include "GUI.hpp"
|
|
#include "I18N.hpp"
|
|
//#include "ConfigWizard.hpp"
|
|
#include "libslic3r_version.h"
|
|
#include "wxExtensions.hpp"
|
|
#include "Widgets/Label.hpp"
|
|
#include "slic3r/GUI/MainFrame.hpp"
|
|
#include "GUI_App.hpp"
|
|
#include "libslic3r/AppConfig.hpp"
|
|
#define MSG_DLG_MAX_SIZE wxSize(-1, FromDIP(464))//notice:ban setting the maximum width value
|
|
namespace Slic3r {
|
|
namespace GUI {
|
|
|
|
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, long style, wxBitmap bitmap, const wxString &forward_str)
|
|
: DPIDialog(parent ? parent : dynamic_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, title, wxDefaultPosition, wxSize(360, -1),wxDEFAULT_DIALOG_STYLE)
|
|
, boldfont(wxGetApp().normal_font())
|
|
, content_sizer(new wxBoxSizer(wxVERTICAL))
|
|
, btn_sizer(new wxBoxSizer(wxHORIZONTAL))
|
|
, m_forward_str(forward_str)
|
|
{
|
|
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
|
|
SetBackgroundColour(0xFFFFFF);
|
|
SetFont(wxGetApp().normal_font());
|
|
CenterOnParent();
|
|
|
|
auto *main_sizer = new wxBoxSizer(wxVERTICAL);
|
|
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
|
auto *rightsizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
//auto *headtext = new wxStaticText(this, wxID_ANY, headline);
|
|
//headtext->SetFont(boldfont);
|
|
// headtext->Wrap(CONTENT_WIDTH*wxGetApp().em_unit());
|
|
//rightsizer->Add(headtext);
|
|
//rightsizer->AddSpacer(VERT_SPACING);
|
|
|
|
rightsizer->Add(content_sizer, 1, wxEXPAND | wxRIGHT, FromDIP(10));
|
|
|
|
logo = new wxStaticBitmap(this, wxID_ANY, bitmap.IsOk() ? bitmap : wxNullBitmap);
|
|
topsizer->Add(LOGO_SPACING, 0, 0, wxEXPAND, 0);
|
|
topsizer->Add(logo, 0, wxTOP, BORDER);
|
|
topsizer->Add(LOGO_GAP, 0, 0, wxEXPAND, 0);
|
|
topsizer->Add(rightsizer, 1, wxTOP | wxEXPAND, BORDER);
|
|
|
|
main_sizer->Add(topsizer, 1, wxEXPAND);
|
|
|
|
m_dsa_sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
btn_sizer->Add(0, 0, 0, wxLEFT, FromDIP(LOGO_SPACING + 64 + LOGO_GAP));
|
|
btn_sizer->Add(m_dsa_sizer, 0, wxEXPAND);
|
|
btn_sizer->AddStretchSpacer();
|
|
main_sizer->Add(btn_sizer, 0, wxBOTTOM | wxRIGHT | wxEXPAND | wxTOP, FromDIP(10));
|
|
|
|
apply_style(style);
|
|
SetSizer(main_sizer);
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
MsgDialog::~MsgDialog()
|
|
{
|
|
for (auto mb : m_buttons) { delete mb.second->buttondata ; delete mb.second; }
|
|
}
|
|
|
|
void MsgDialog::show_dsa_button(wxString const &title)
|
|
{
|
|
m_checkbox_dsa = new CheckBox(this);
|
|
m_dsa_sizer->Add(m_checkbox_dsa, 0, wxALIGN_CENTER);
|
|
m_checkbox_dsa->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& e) {
|
|
auto event = wxCommandEvent(EVT_CHECKBOX_CHANGE);
|
|
event.SetInt(m_checkbox_dsa->GetValue()?1:0);
|
|
event.SetEventObject(this);
|
|
wxPostEvent(this, event);
|
|
e.Skip();
|
|
});
|
|
|
|
auto m_text_dsa = new wxStaticText(this, wxID_ANY, title.IsEmpty() ? _L("Don't show again") : title, wxDefaultPosition, wxDefaultSize, 0);
|
|
auto on_toggle = [this]() {
|
|
m_checkbox_dsa->SetValue(!m_checkbox_dsa->GetValue());
|
|
wxCommandEvent ev(wxEVT_TOGGLEBUTTON, m_checkbox_dsa->GetId());
|
|
ev.SetEventObject(m_checkbox_dsa);
|
|
m_checkbox_dsa->GetEventHandler()->ProcessEvent(ev);
|
|
};
|
|
m_text_dsa->Bind(wxEVT_LEFT_DOWN, [on_toggle](wxMouseEvent& e) {if(!e.LeftDClick()) on_toggle();});
|
|
m_text_dsa->Bind(wxEVT_LEFT_DCLICK, [on_toggle](wxMouseEvent& e) {on_toggle();});
|
|
|
|
m_dsa_sizer->Add(m_text_dsa, 0, wxALL | wxALIGN_CENTER, FromDIP(2));
|
|
m_dsa_sizer->AddSpacer(FromDIP(10)); // spacing after checkbox
|
|
m_text_dsa->SetFont(::Label::Body_13);
|
|
m_text_dsa->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#323A3D")));
|
|
btn_sizer->Layout();
|
|
Fit();
|
|
}
|
|
|
|
bool MsgDialog::get_checkbox_state()
|
|
{
|
|
if (m_checkbox_dsa) {
|
|
return m_checkbox_dsa->GetValue();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MsgDialog::on_dpi_changed(const wxRect &suggested_rect)
|
|
{
|
|
if (m_buttons.size() > 0) {
|
|
MsgButtonsHash::iterator i = m_buttons.begin();
|
|
|
|
while (i != m_buttons.end()) {
|
|
MsgButton *bd = i->second;
|
|
/* ORCA not required since all buttons has same size and Rescale re applies its style
|
|
wxSize bsize;
|
|
|
|
|
|
switch (bd->buttondata->type) {
|
|
case ButtonSizeNormal:bsize = MSG_DIALOG_BUTTON_SIZE;break;
|
|
case ButtonSizeMiddle: bsize = MSG_DIALOG_MIDDLE_BUTTON_SIZE; break;
|
|
case ButtonSizeLong: bsize = MSG_DIALOG_LONG_BUTTON_SIZE; break;
|
|
default: break;
|
|
}
|
|
|
|
bd->buttondata->button->SetMinSize(bsize);
|
|
*/
|
|
bd->buttondata->button->Rescale();
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MsgDialog::SetButtonLabel(wxWindowID btn_id, const wxString& label, bool set_focus/* = false*/)
|
|
{
|
|
if (Button* btn = get_button(btn_id)) {
|
|
btn->SetLabel(label);
|
|
if (set_focus)
|
|
btn->SetFocus();
|
|
}
|
|
}
|
|
|
|
Button* MsgDialog::add_button(wxWindowID btn_id, bool set_focus /*= false*/, const wxString& label/* = wxString()*/)
|
|
{
|
|
Button* btn = new Button(this, label, "", 0, 0, btn_id);
|
|
/* ORCA not required since all buttons has same size and Rescale re applies its style
|
|
ButtonSizeType type;
|
|
|
|
if (label.length() < 5) {
|
|
type = ButtonSizeNormal;
|
|
btn->SetMinSize(MSG_DIALOG_BUTTON_SIZE); // ?????
|
|
}
|
|
else if (label.length() >= 5 && label.length() < 8) {
|
|
type = ButtonSizeMiddle;
|
|
btn->SetMinSize(MSG_DIALOG_MIDDLE_BUTTON_SIZE);
|
|
}
|
|
else if (label.length() >= 8 && label.length() < 12) {
|
|
type = ButtonSizeMiddle;
|
|
btn->SetMinSize(MSG_DIALOG_LONG_BUTTON_SIZE);
|
|
} else {
|
|
type = ButtonSizeLong;
|
|
btn->SetMinSize(MSG_DIALOG_LONGER_BUTTON_SIZE);
|
|
}
|
|
*/
|
|
|
|
if (btn_id == wxID_OK || btn_id == wxID_YES || btn_id == wxFORWARD) {
|
|
btn->SetStyle(ButtonStyle::Confirm, ButtonType::Choice);
|
|
}
|
|
|
|
if (btn_id == wxID_CANCEL || btn_id == wxID_NO) {
|
|
btn->SetStyle(ButtonStyle::Regular, ButtonType::Choice);
|
|
}
|
|
|
|
if (set_focus)
|
|
btn->SetFocus();
|
|
btn_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(ButtonProps::ChoiceButtonGap()));
|
|
btn->Bind(wxEVT_BUTTON, [this, btn_id](wxCommandEvent&) { EndModal(btn_id); });
|
|
|
|
MsgButton *mb = new MsgButton;
|
|
ButtonData *bd = new ButtonData;
|
|
|
|
bd->button = btn;
|
|
//bd->type = type;
|
|
|
|
// m_buttons.size() is a size_t, which does not match the %d conversion
|
|
mb->id = wxString(std::to_string(m_buttons.size()));
|
|
mb->buttondata = bd;
|
|
m_buttons[ wxString(std::to_string(m_buttons.size()))] = mb;
|
|
return btn;
|
|
};
|
|
|
|
Button* MsgDialog::get_button(wxWindowID btn_id){
|
|
return static_cast<Button*>(FindWindowById(btn_id, this));
|
|
}
|
|
|
|
void MsgDialog::apply_style(long style)
|
|
{
|
|
if (style & wxFORWARD)
|
|
add_button(wxFORWARD, true, _L("Go to") + " " + m_forward_str);
|
|
if (style & wxOK) {
|
|
if (style & wxFORWARD) { add_button(wxID_CANCEL, false, _L("Later")); }
|
|
else {
|
|
add_button(wxID_OK, true, _L("OK"));
|
|
}
|
|
}
|
|
if (style & wxYES) add_button(wxID_YES, true, _L("Yes"));
|
|
if (style & wxNO) add_button(wxID_NO, false,_L("No"));
|
|
if (style & wxCANCEL) add_button(wxID_CANCEL, false, _L("Cancel"));
|
|
|
|
logo->SetBitmap( create_scaled_bitmap(style & wxAPPLY ? "completed" :
|
|
style & wxICON_WARNING ? "exclamation" : // ORCA "exclamation" used for dialogs "obj_warning" used for 16x16 areas
|
|
style & wxICON_INFORMATION ? "info" :
|
|
style & wxICON_QUESTION ? "question" : "OrcaSlicer", this, 64, style & wxICON_ERROR));
|
|
}
|
|
|
|
void MsgDialog::finalize()
|
|
{
|
|
GetSizer()->SetSizeHints(this);
|
|
Layout();
|
|
Fit();
|
|
CenterOnParent();
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
|
|
// A placeholder-parser caret line, pointing at the column where parsing failed.
|
|
static bool is_caret_line(const std::string &line)
|
|
{
|
|
return std::count(line.begin(), line.end(), '^') == 1 &&
|
|
std::all_of(line.begin(), line.end(), [](char c) { return c == ' ' || c == '^'; });
|
|
}
|
|
|
|
// Tag each line as a code excerpt (a caret line or the source line above one) that must stay
|
|
// monospaced for the '^' to align.
|
|
static std::vector<std::pair<std::string, bool>> classify_code_lines(const std::string &msg)
|
|
{
|
|
std::vector<std::string> lines;
|
|
boost::split(lines, msg, boost::is_any_of("\n"));
|
|
for (std::string &line : lines)
|
|
if (!line.empty() && line.back() == '\r')
|
|
line.pop_back();
|
|
|
|
std::vector<std::pair<std::string, bool>> tagged;
|
|
tagged.reserve(lines.size());
|
|
for (size_t i = 0; i < lines.size(); ++i) {
|
|
bool is_code = is_caret_line(lines[i]) || (i + 1 < lines.size() && is_caret_line(lines[i + 1]));
|
|
tagged.emplace_back(std::move(lines[i]), is_code);
|
|
}
|
|
return tagged;
|
|
}
|
|
|
|
// Keeps whitespace literal so the caret's leading spaces survive.
|
|
// Used inside <code>, which supplies the fixed face. <pre> does both but adds a blank line above it.
|
|
class CodeExcerptTagHandler : public wxHtmlWinTagHandler
|
|
{
|
|
public:
|
|
wxString GetSupportedTags() override { return wxT("EXCERPT"); }
|
|
bool HandleTag(const wxHtmlTag &tag) override
|
|
{
|
|
const wxHtmlWinParser::WhitespaceMode ws = m_WParser->GetWhitespaceMode();
|
|
m_WParser->SetWhitespaceMode(wxHtmlWinParser::Whitespace_Pre);
|
|
ParseInner(tag);
|
|
m_WParser->SetWhitespaceMode(ws);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Render the message as HTML, monospacing only the code excerpts.
|
|
static std::string format_parser_error_html(const std::string &msg)
|
|
{
|
|
std::string out;
|
|
for (const auto &[text, is_code] : classify_code_lines(msg)) {
|
|
if (!out.empty()) out += "<br>"; // join, not trail; a trailing <br> forces a scrollbar
|
|
std::string escaped = xml_escape(text);
|
|
if (is_code)
|
|
out += "<code><excerpt>" + escaped + "</excerpt></code>";
|
|
else
|
|
out += escaped;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// Measure each line in the font it will render in, so the dialog fits the longest line without slack.
|
|
static wxSize measure_mixed_text(wxWindow *parent, const std::string &msg, const wxFont &prose_font, const wxFont &code_font)
|
|
{
|
|
wxClientDC dc(parent);
|
|
int width = 0, height = 0;
|
|
for (const auto &[text, is_code] : classify_code_lines(msg)) {
|
|
dc.SetFont(is_code ? code_font : prose_font);
|
|
width = std::max(width, dc.GetTextExtent(wxString::FromUTF8(text.c_str())).GetWidth());
|
|
height += dc.GetCharHeight();
|
|
}
|
|
return wxSize(width, height);
|
|
}
|
|
|
|
// Text shown as HTML, so that mouse selection and Ctrl-V to copy will work.
|
|
static void add_msg_content(wxWindow *parent,
|
|
wxBoxSizer *content_sizer,
|
|
wxString msg,
|
|
bool has_code_excerpts = false,
|
|
bool is_marked_msg = false,
|
|
const wxString &link_text = "",
|
|
std::function<void(const wxString &)> link_callback = nullptr)
|
|
{
|
|
wxHtmlWindow* html = new wxHtmlWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
|
|
html->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
|
|
|
|
// count lines in the message
|
|
int msg_lines = 0;
|
|
if (!has_code_excerpts) {
|
|
int line_len = 55;// count of symbols in one line
|
|
int start_line = 0;
|
|
for (auto i = msg.begin(); i != msg.end(); ++i) {
|
|
if (*i == '\n') {
|
|
int cur_line_len = i - msg.begin() - start_line;
|
|
start_line = i - msg.begin();
|
|
if (cur_line_len == 0 || line_len > cur_line_len)
|
|
msg_lines++;
|
|
else
|
|
msg_lines += std::lround((double)(cur_line_len) / line_len);
|
|
}
|
|
}
|
|
msg_lines++;
|
|
}
|
|
|
|
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
|
wxFont monospace = wxGetApp().code_font();
|
|
wxColour text_clr = wxGetApp().get_label_clr_default();
|
|
wxColour bgr_clr = parent->GetBackgroundColour();
|
|
auto text_clr_str = encode_color(ColorRGB(text_clr.Red(), text_clr.Green(), text_clr.Blue()));
|
|
auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()));
|
|
const int font_size = font.GetPointSize();
|
|
int size[] = { font_size, font_size, font_size, font_size, font_size, font_size, font_size };
|
|
html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size);
|
|
html->SetBorders(2);
|
|
|
|
// calculate html page size from text
|
|
wxSize page_size;
|
|
int em = wxGetApp().em_unit();
|
|
if (!wxGetApp().mainframe) {
|
|
// If mainframe is nullptr, it means that GUI_App::on_init_inner() isn't completed
|
|
// (We just show information dialog about configuration version now)
|
|
// And as a result the em_unit value wasn't created yet
|
|
// So, calculate it from the scale factor of Dialog
|
|
#if defined(__WXGTK__)
|
|
// Linux specific issue : get_dpi_for_window(this) still doesn't responce to the Display's scale in new wxWidgets(3.1.3).
|
|
// So, initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
|
|
em = std::max<size_t>(10, parent->GetTextExtent("m").x - 1);
|
|
#else
|
|
double scale_factor = (double)get_dpi_for_window(parent) / (double)DPI_DEFAULT;
|
|
em = std::max<size_t>(10, 10.0f * scale_factor);
|
|
#endif // __WXGTK__
|
|
}
|
|
auto info_width = 68 * em;
|
|
// if message containes the table
|
|
if (msg.Contains("<tr>")) {
|
|
int lines = msg.Freq('\n') + 1;
|
|
int pos = 0;
|
|
while (pos < (int)msg.Len() && pos != wxNOT_FOUND) {
|
|
pos = msg.find("<tr>", pos + 1);
|
|
lines += 2;
|
|
}
|
|
int page_height = std::min(int(font.GetPixelSize().y + 2) * lines, info_width);
|
|
page_size = wxSize(info_width, page_height);
|
|
}
|
|
else {
|
|
wxSize msg_sz;
|
|
if (has_code_excerpts) {
|
|
msg_sz = measure_mixed_text(parent, msg.ToUTF8().data(), font, monospace);
|
|
} else {
|
|
wxClientDC dc(parent);
|
|
dc.SetFont(font); // ORCA without this it calculates bigger size
|
|
msg_sz = dc.GetMultiLineTextExtent(msg);
|
|
}
|
|
msg_sz += parent->FromDIP(wxSize(10,5)); // added extra spacing to prevent wrapping
|
|
|
|
int page_height = msg_sz.GetY();
|
|
// Reserve the horizontal scrollbar's height, or it clips the last line.
|
|
if (msg_sz.GetX() > info_width)
|
|
page_height += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y, parent);
|
|
page_size = wxSize(std::min(msg_sz.GetX(), info_width), std::min(page_height, info_width));
|
|
// Extra line breaks in message dialog
|
|
if (link_text.IsEmpty() && !link_callback && is_marked_msg == false && !has_code_excerpts) {//for common text
|
|
html->Destroy();
|
|
if (msg_sz.GetX() < info_width) {//No need for line breaks
|
|
info_width = msg_sz.GetX();
|
|
}
|
|
wxScrolledWindow *scrolledWindow = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
|
scrolledWindow->SetBackgroundColour(*wxWHITE);
|
|
scrolledWindow->SetScrollRate(0, scrolledWindow->FromDIP(20));
|
|
scrolledWindow->EnableScrolling(false, true);
|
|
wxBoxSizer *sizer_scrolled = new wxBoxSizer(wxHORIZONTAL);
|
|
Label *wrapped_text = new Label(scrolledWindow, font, msg, LB_AUTO_WRAP, wxSize(info_width, -1));
|
|
wrapped_text->SetMinSize(wxSize(info_width, -1));
|
|
wrapped_text->SetMaxSize(wxSize(info_width, -1));
|
|
wrapped_text->Wrap(info_width);
|
|
sizer_scrolled->Add(wrapped_text, wxALIGN_LEFT ,0);
|
|
sizer_scrolled->AddSpacer(5);
|
|
sizer_scrolled->AddStretchSpacer();
|
|
scrolledWindow->SetSizer(sizer_scrolled);
|
|
auto info_height = 48 * em;
|
|
if (sizer_scrolled->GetMinSize().GetHeight() < info_height) {
|
|
info_height = sizer_scrolled->GetMinSize().GetHeight();
|
|
}
|
|
scrolledWindow->SetMinSize(wxSize(info_width, info_height));
|
|
scrolledWindow->SetMaxSize(wxSize(info_width, info_height));
|
|
scrolledWindow->FitInside();
|
|
content_sizer->Add(scrolledWindow, 1, wxEXPAND | wxRIGHT, 8);
|
|
return;
|
|
}
|
|
}
|
|
html->SetMinSize(page_size);
|
|
|
|
std::string msg_escaped;
|
|
if (has_code_excerpts) {
|
|
html->GetParser()->AddTagHandler(new CodeExcerptTagHandler());
|
|
msg_escaped = format_parser_error_html(msg.ToUTF8().data());
|
|
} else {
|
|
msg_escaped = xml_escape(msg.ToUTF8().data(), is_marked_msg);
|
|
boost::replace_all(msg_escaped, "\r\n", "<br>");
|
|
boost::replace_all(msg_escaped, "\n", "<br>");
|
|
}
|
|
|
|
if (!link_text.IsEmpty() && link_callback) {
|
|
msg_escaped += "<span><a href=\"#\" style=\"color:rgb(0, 150, 136); text-decoration:underline;\">" + std::string(link_text.ToUTF8().data()) + "</a></span>";
|
|
}
|
|
|
|
html->SetPage("<html><body bgcolor=\"" + bgr_clr_str + "\"><font color=\"" + text_clr_str + "\">" + wxString::FromUTF8(msg_escaped.data()) + "</font></body></html>");
|
|
content_sizer->Add(html, 1, wxEXPAND|wxRIGHT, 8);
|
|
wxGetApp().UpdateDarkUIWin(html);
|
|
|
|
html->Bind(wxEVT_HTML_LINK_CLICKED, [=](wxHtmlLinkEvent& event) {
|
|
if (link_callback)
|
|
link_callback(event.GetLinkInfo().GetHref());
|
|
});
|
|
}
|
|
|
|
// ErrorDialog
|
|
|
|
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &temp_msg, bool has_code_excerpts)
|
|
: MsgDialog(parent, wxString::Format(_(L("%s error")), SLIC3R_APP_FULL_NAME),
|
|
wxString::Format(_(L("%s has encountered an error")), SLIC3R_APP_FULL_NAME), wxOK)
|
|
, msg(temp_msg)
|
|
{
|
|
add_msg_content(this, content_sizer, msg, has_code_excerpts);
|
|
|
|
// Use a small bitmap for code excerpts, which cannot wrap and so need the width.
|
|
logo->SetBitmap(create_scaled_bitmap("OrcaSlicer_192px_grayscale.png", this, has_code_excerpts ? 48 : /*1*/64));
|
|
|
|
SetMaxSize(MSG_DLG_MAX_SIZE);
|
|
|
|
finalize();
|
|
}
|
|
|
|
// WarningDialog
|
|
|
|
WarningDialog::WarningDialog(wxWindow *parent,
|
|
const wxString& message,
|
|
const wxString& caption/* = wxEmptyString*/,
|
|
long style/* = wxOK*/)
|
|
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s warning"), SLIC3R_APP_FULL_NAME) : caption,
|
|
wxString::Format(_L("%s has a warning")+":", SLIC3R_APP_FULL_NAME), style)
|
|
{
|
|
add_msg_content(this, content_sizer, message);
|
|
finalize();
|
|
}
|
|
|
|
#if 1
|
|
// MessageDialog
|
|
|
|
MessageDialog::MessageDialog(wxWindow* parent,
|
|
const wxString& message,
|
|
const wxString& caption/* = wxEmptyString*/,
|
|
long style /* = wxOK*/,
|
|
const wxString &forward_str /* = wxEmptyString*/,
|
|
const wxString &link_text /* = wxEmptyString*/,
|
|
std::function<void(const wxString &)> link_callback /* = nullptr*/)
|
|
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_FULL_NAME) : caption, wxEmptyString, style, wxBitmap(),forward_str)
|
|
{
|
|
add_msg_content(this, content_sizer, message, false, false, link_text, link_callback);
|
|
SetMaxSize(MSG_DLG_MAX_SIZE);
|
|
finalize();
|
|
}
|
|
|
|
|
|
// RichMessageDialog
|
|
|
|
RichMessageDialog::RichMessageDialog(wxWindow* parent,
|
|
const wxString& message,
|
|
const wxString& caption/* = wxEmptyString*/,
|
|
long style/* = wxOK*/)
|
|
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_FULL_NAME) : caption, wxEmptyString, style)
|
|
{
|
|
add_msg_content(this, content_sizer, message);
|
|
finalize();
|
|
}
|
|
|
|
int RichMessageDialog::ShowModal()
|
|
{
|
|
if (!m_checkBoxText.IsEmpty()) {
|
|
show_dsa_button(m_checkBoxText);
|
|
m_checkbox_dsa->SetValue(m_checkBoxValue);
|
|
}
|
|
Layout();
|
|
|
|
return MsgDialog::ShowModal();
|
|
}
|
|
|
|
bool RichMessageDialog::IsCheckBoxChecked() const
|
|
{
|
|
if (m_checkbox_dsa)
|
|
return m_checkbox_dsa->GetValue();
|
|
|
|
return m_checkBoxValue;
|
|
}
|
|
#endif
|
|
|
|
// InfoDialog
|
|
InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& msg, bool is_marked_msg/* = false*/, long style/* = wxOK | wxICON_INFORMATION*/)
|
|
: MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_FULL_NAME), title, style)
|
|
, msg(msg)
|
|
{
|
|
add_msg_content(this, content_sizer, msg, false, is_marked_msg);
|
|
finalize();
|
|
}
|
|
|
|
wxString get_wraped_wxString(const wxString& in, size_t line_len /*=80*/)
|
|
{
|
|
wxString out;
|
|
|
|
for (size_t i = 0; i < in.size();) {
|
|
// Overwrite the character (space or newline) starting at ibreak?
|
|
bool overwrite = false;
|
|
// UTF8 representation of wxString.
|
|
// Where to break the line, index of character at the start of a UTF-8 sequence.
|
|
size_t ibreak = size_t(-1);
|
|
// Overwrite the character at ibreak (it is a whitespace) or not?
|
|
size_t j = i;
|
|
for (size_t cnt = 0; j < in.size();) {
|
|
if (bool newline = in[j] == '\n'; in[j] == ' ' || in[j] == '\t' || newline) {
|
|
// Overwrite the whitespace.
|
|
ibreak = j ++;
|
|
overwrite = true;
|
|
if (newline)
|
|
break;
|
|
} else if (in[j] == '/'
|
|
#ifdef _WIN32
|
|
|| in[j] == '\\'
|
|
#endif // _WIN32
|
|
) {
|
|
// Insert after the slash.
|
|
ibreak = ++ j;
|
|
overwrite = false;
|
|
} else
|
|
j += get_utf8_sequence_length(in.c_str() + j, in.size() - j);
|
|
if (++ cnt == line_len) {
|
|
if (ibreak == size_t(-1)) {
|
|
ibreak = j;
|
|
overwrite = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (j == in.size()) {
|
|
out.append(in.begin() + i, in.end());
|
|
break;
|
|
}
|
|
assert(ibreak != size_t(-1));
|
|
out.append(in.begin() + i, in.begin() + ibreak);
|
|
out.append('\n');
|
|
i = ibreak;
|
|
if (overwrite)
|
|
++ i;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
// InfoDialog
|
|
DownloadDialog::DownloadDialog(wxWindow *parent, const wxString &msg, const wxString &title, bool is_marked_msg /* = false*/, long style /* = wxOK | wxICON_INFORMATION*/)
|
|
: MsgDialog(parent, title, msg, style), msg(msg)
|
|
{
|
|
add_button(wxID_YES, true, _L("Download"));
|
|
add_button(wxID_CANCEL, true, _L("Skip"));
|
|
|
|
finalize();
|
|
}
|
|
|
|
|
|
void DownloadDialog::SetExtendedMessage(const wxString &extendedMessage)
|
|
{
|
|
add_msg_content(this, content_sizer, msg + "\n" + extendedMessage, false, false);
|
|
Layout();
|
|
Fit();
|
|
}
|
|
|
|
DeleteConfirmDialog::DeleteConfirmDialog(wxWindow *parent, const wxString &title, const wxString &msg)
|
|
: DPIDialog(parent ? parent : nullptr, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
|
{
|
|
this->SetBackgroundColour(*wxWHITE);
|
|
this->SetSize(wxSize(FromDIP(450), FromDIP(200)));
|
|
|
|
wxBoxSizer *m_main_sizer = new wxBoxSizer(wxVERTICAL);
|
|
// top line
|
|
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
|
m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA));
|
|
m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
|
|
m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
|
|
|
|
m_msg_text = new wxStaticText(this, wxID_ANY, msg);
|
|
m_main_sizer->Add(m_msg_text, 0, wxEXPAND | wxALL, FromDIP(10));
|
|
|
|
wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL);
|
|
bSizer_button->Add(0, 0, 1, wxEXPAND, 0);
|
|
|
|
m_cancel_btn = new Button(this, _L("Cancel"));
|
|
m_cancel_btn->SetStyle(ButtonStyle::Regular, ButtonType::Choice);
|
|
bSizer_button->Add(m_cancel_btn, 0, wxRIGHT | wxBOTTOM, FromDIP(ButtonProps::ChoiceButtonGap()));
|
|
|
|
m_del_btn = new Button(this, _L("Delete"));
|
|
m_del_btn->SetStyle(ButtonStyle::Alert, ButtonType::Choice);
|
|
bSizer_button->Add(m_del_btn, 0, wxRIGHT | wxBOTTOM, FromDIP(ButtonProps::ChoiceButtonGap()));
|
|
|
|
m_main_sizer->Add(bSizer_button, 0, wxEXPAND, 0);
|
|
m_del_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { EndModal(wxID_OK); });
|
|
m_cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { EndModal(wxID_CANCEL); });
|
|
|
|
SetSizerAndFit(m_main_sizer);
|
|
Layout();
|
|
Fit();
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
DeleteConfirmDialog::~DeleteConfirmDialog() {}
|
|
|
|
|
|
void DeleteConfirmDialog::on_dpi_changed(const wxRect &suggested_rect) {}
|
|
|
|
|
|
Newer3mfVersionDialog::Newer3mfVersionDialog(wxWindow *parent, const Semver *file_version, const Semver *cloud_version, wxString new_keys)
|
|
: DPIDialog(parent ? parent : nullptr, wxID_ANY, wxString(SLIC3R_APP_FULL_NAME " - ") + _L("Newer 3MF version"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
|
, m_file_version(file_version)
|
|
, m_cloud_version(cloud_version)
|
|
, m_new_keys(new_keys)
|
|
{
|
|
this->SetBackgroundColour(*wxWHITE);
|
|
|
|
wxBoxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
|
|
// top line
|
|
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
|
m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA));
|
|
main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
|
|
main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
|
|
|
|
wxBoxSizer * content_sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
wxStaticBitmap *info_bitmap = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("info", nullptr, 64), wxDefaultPosition, wxSize(FromDIP(70), FromDIP(70)), 0);
|
|
wxBoxSizer * msg_sizer = get_msg_sizer();
|
|
content_sizer->Add(info_bitmap, 0, wxEXPAND | wxALL, FromDIP(5));
|
|
content_sizer->Add(msg_sizer, 0, wxEXPAND | wxALL, FromDIP(5));
|
|
main_sizer->Add(content_sizer, 0, wxEXPAND | wxALL, FromDIP(5));
|
|
main_sizer->Add(get_btn_sizer(), 0, wxEXPAND | wxALL, FromDIP(5));
|
|
|
|
this->SetSizerAndFit(main_sizer);
|
|
Layout();
|
|
Fit();
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
wxBoxSizer *Newer3mfVersionDialog::get_msg_sizer()
|
|
{
|
|
wxBoxSizer *vertical_sizer = new wxBoxSizer(wxVERTICAL);
|
|
bool file_version_newer = (*m_file_version) > (*m_cloud_version);
|
|
wxStaticText *text1;
|
|
wxBoxSizer * horizontal_sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
wxString msg_str;
|
|
if (file_version_newer) {
|
|
text1 = new wxStaticText(this, wxID_ANY, _L("The 3MF file version is in Beta and it is newer than the current OrcaSlicer version."));
|
|
wxStaticText * text2 = new wxStaticText(this, wxID_ANY, _L("If you would like to try Orca Slicer Beta, you may click to"));
|
|
// ORCA standardized HyperLink
|
|
HyperLink * github_link = new HyperLink(this, _L("Download Beta Version"), "https://github.com/SoftFever/OrcaSlicer/releases");
|
|
horizontal_sizer->Add(text2, 0, wxEXPAND, 0);
|
|
horizontal_sizer->Add(github_link, 0, wxEXPAND | wxLEFT, 5);
|
|
|
|
} else {
|
|
text1 = new wxStaticText(this, wxID_ANY, _L("The 3MF file version is newer than the current OrcaSlicer version."));
|
|
wxStaticText *text2 = new wxStaticText(this, wxID_ANY, _L("Updating your OrcaSlicer could enable all functionality in the 3MF file."));
|
|
horizontal_sizer->Add(text2, 0, wxEXPAND, 0);
|
|
}
|
|
Semver app_version = *(Semver::parse(SLIC3R_VERSION));
|
|
wxStaticText *cur_version = new wxStaticText(this, wxID_ANY, _L("Current Version: ") + app_version.to_string());
|
|
|
|
vertical_sizer->Add(text1, 0, wxEXPAND | wxTOP, FromDIP(5));
|
|
vertical_sizer->Add(horizontal_sizer, 0, wxEXPAND | wxTOP, FromDIP(5));
|
|
vertical_sizer->Add(cur_version, 0, wxEXPAND | wxTOP, FromDIP(5));
|
|
if (!file_version_newer) {
|
|
wxStaticText *latest_version = new wxStaticText(this, wxID_ANY, _L("Latest Version: ") + m_cloud_version->to_string());
|
|
vertical_sizer->Add(latest_version, 0, wxEXPAND | wxTOP, FromDIP(5));
|
|
}
|
|
|
|
wxStaticText *unrecognized_keys = new wxStaticText(this, wxID_ANY, m_new_keys);
|
|
vertical_sizer->Add(unrecognized_keys, 0, wxEXPAND | wxTOP, FromDIP(10));
|
|
|
|
return vertical_sizer;
|
|
}
|
|
|
|
wxBoxSizer *Newer3mfVersionDialog::get_btn_sizer()
|
|
{
|
|
wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
horizontal_sizer->Add(0, 0, 1, wxEXPAND, 0);
|
|
|
|
bool file_version_newer = (*m_file_version) > (*m_cloud_version);
|
|
if (!file_version_newer) {
|
|
m_update_btn = new Button(this, _L_CONTEXT(L_CONTEXT("Update", "Software"), "Software"));
|
|
m_update_btn->SetStyle(ButtonStyle::Regular, ButtonType::Choice);
|
|
horizontal_sizer->Add(m_update_btn, 0, wxRIGHT, FromDIP(ButtonProps::ChoiceButtonGap()));
|
|
|
|
m_update_btn->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
|
|
EndModal(wxID_OK);
|
|
if (wxGetApp().app_config->has("app", "cloud_software_url")) {
|
|
std::string download_url = wxGetApp().app_config->get("app", "cloud_software_url");
|
|
wxLaunchDefaultBrowser(download_url);
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Bambu Studio conf has no cloud_software_url and file_version: " << m_file_version->to_string()
|
|
<< " and cloud_version: " << m_cloud_version->to_string();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (!file_version_newer) {
|
|
m_later_btn = new Button(this, _L("Not for now"));
|
|
m_later_btn->SetStyle(ButtonStyle::Regular, ButtonType::Choice);
|
|
} else {
|
|
m_later_btn = new Button(this, _L("OK"));
|
|
m_later_btn->SetStyle(ButtonStyle::Confirm, ButtonType::Choice);
|
|
}
|
|
horizontal_sizer->Add(m_later_btn, 0, wxRIGHT, FromDIP(ButtonProps::ChoiceButtonGap()));
|
|
m_later_btn->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
|
|
EndModal(wxID_OK);
|
|
});
|
|
return horizontal_sizer;
|
|
}
|
|
|
|
NetworkErrorDialog::NetworkErrorDialog(wxWindow* parent)
|
|
: DPIDialog(parent ? parent : nullptr, wxID_ANY, _L("Server Exception"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
|
{
|
|
this->SetBackgroundColour(*wxWHITE);
|
|
|
|
wxBoxSizer* sizer_main = new wxBoxSizer(wxVERTICAL);
|
|
|
|
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
|
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
|
|
|
wxBoxSizer* sizer_bacis_text = new wxBoxSizer(wxVERTICAL);
|
|
|
|
m_text_basic = new Label(this, _L("The server is unable to respond. Please click the link below to check the server status."));
|
|
m_text_basic->SetForegroundColour(0x323A3C);
|
|
m_text_basic->SetMinSize(wxSize(FromDIP(470), -1));
|
|
m_text_basic->SetMaxSize(wxSize(FromDIP(470), -1));
|
|
m_text_basic->Wrap(FromDIP(470));
|
|
m_text_basic->SetFont(::Label::Body_14);
|
|
sizer_bacis_text->Add(m_text_basic, 0, wxALL, 0);
|
|
|
|
|
|
wxBoxSizer* sizer_link = new wxBoxSizer(wxVERTICAL);
|
|
|
|
// ORCA standardized HyperLink
|
|
m_link_server_state = new HyperLink(this, _L("Check the status of current system services"), wxGetApp().link_to_network_check());
|
|
m_link_server_state->SetFont(::Label::Body_13);
|
|
|
|
sizer_link->Add(m_link_server_state, 0, wxALL, 0);
|
|
|
|
|
|
wxBoxSizer* sizer_help = new wxBoxSizer(wxVERTICAL);
|
|
|
|
m_text_proposal = new Label(this, _L("If the server is in a fault state, you can temporarily use offline printing or local network printing."));
|
|
m_text_proposal->SetMinSize(wxSize(FromDIP(470), -1));
|
|
m_text_proposal->SetMaxSize(wxSize(FromDIP(470), -1));
|
|
m_text_proposal->Wrap(FromDIP(470));
|
|
m_text_proposal->SetFont(::Label::Body_14);
|
|
m_text_proposal->SetForegroundColour(0x323A3C);
|
|
|
|
// ORCA standardized HyperLink
|
|
m_text_wiki = new HyperLink(this, _L("How to use LAN only mode"), wxGetApp().link_to_lan_only_wiki());
|
|
m_text_wiki->SetFont(::Label::Body_13);
|
|
|
|
sizer_help->Add(m_text_proposal, 0, wxEXPAND, 0);
|
|
sizer_help->Add(m_text_wiki, 0, wxALL, 0);
|
|
|
|
wxBoxSizer* sizer_button = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
/*dont show again*/
|
|
auto checkbox = new ::CheckBox(this);
|
|
checkbox->SetValue(false);
|
|
|
|
|
|
auto checkbox_title = new Label(this, _L("Don't show this dialog again"));
|
|
checkbox_title->SetForegroundColour(0x323A3C);
|
|
checkbox_title->SetFont(::Label::Body_14);
|
|
checkbox_title->Wrap(-1);
|
|
|
|
checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, checkbox](wxCommandEvent &e) {
|
|
m_show_again = checkbox->GetValue();
|
|
e.Skip();
|
|
});
|
|
|
|
m_button_confirm = new Button(this, _L("Confirm"));
|
|
m_button_confirm->SetStyle(ButtonStyle::Confirm, ButtonType::Choice);
|
|
m_button_confirm->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {EndModal(wxCLOSE);});
|
|
|
|
sizer_button->Add(checkbox, 0, wxALL, 5);
|
|
sizer_button->Add(checkbox_title, 0, wxALL, 5);
|
|
sizer_button->Add(0, 0, 1, wxEXPAND, 5);
|
|
sizer_button->Add(m_button_confirm, 0, wxALL, FromDIP(ButtonProps::ChoiceButtonGap()));
|
|
|
|
sizer_main->Add(m_line_top, 0, wxEXPAND, 0);
|
|
sizer_main->Add(0, 0, 0, wxTOP, 20);
|
|
sizer_main->Add(sizer_bacis_text, 0, wxEXPAND | wxLEFT | wxRIGHT, 15);
|
|
sizer_main->Add(0, 0, 0, wxTOP, 6);
|
|
sizer_main->Add(sizer_link, 0, wxLEFT | wxRIGHT, 15);
|
|
sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(20));
|
|
sizer_main->Add(sizer_help, 1, wxLEFT | wxRIGHT, 15);
|
|
sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(20));
|
|
sizer_main->Add(sizer_button, 1, wxEXPAND | wxLEFT | wxRIGHT, 15);
|
|
sizer_main->Add(0, 0, 0, wxTOP, 18);
|
|
|
|
SetSizerAndFit(sizer_main);
|
|
Layout();
|
|
Centre(wxBOTH);
|
|
}
|
|
|
|
|
|
FilamentWarningDialog::FilamentWarningDialog(wxWindow *parent, const wxString &title, std::vector<FilamentWarningInfo> infos)
|
|
: MsgDialog(parent, title.IsEmpty() ? wxString::Format(_L("%s warning"), SLIC3R_APP_FULL_NAME) : title, wxEmptyString, wxOK | wxICON_WARNING), m_messages(infos)
|
|
{
|
|
BuildContent();
|
|
finalize();
|
|
}
|
|
|
|
void FilamentWarningDialog::BuildContent()
|
|
{
|
|
wxBoxSizer *messages_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
int message_count = 0;
|
|
for (int i = 0; i < m_messages.size(); i++)
|
|
{
|
|
const wxString &message = m_messages[i].info_msg;
|
|
const wxString &wiki_url = m_messages[i].wiki_url;
|
|
if (message_count > 0) { messages_sizer->AddSpacer(FromDIP(10)); }
|
|
|
|
if (wiki_url.IsEmpty()) {
|
|
// No wiki link - just display as regular text
|
|
Label *text = new Label(this, message);
|
|
text->SetFont(::Label::Body_12);
|
|
text->Wrap(FromDIP(400));
|
|
messages_sizer->Add(text, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(5));
|
|
} else {
|
|
Label *link = new Label(this, message + " " + _L("Please refer to Wiki before use->"));
|
|
link->SetForegroundColour(wxColour(8, 153, 46));
|
|
link->SetFont(::Label::Body_12);
|
|
link->Wrap(FromDIP(400));
|
|
link->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { SetCursor(wxCURSOR_HAND); });
|
|
link->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { SetCursor(wxCURSOR_ARROW); });
|
|
link->Bind(wxEVT_LEFT_DOWN, [wiki_url](auto &event) { wxLaunchDefaultBrowser(wiki_url); });
|
|
messages_sizer->Add(link, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(5));
|
|
}
|
|
message_count++;
|
|
}
|
|
|
|
content_sizer->Add(messages_sizer, 1, wxEXPAND | wxALL, FromDIP(5));
|
|
}
|
|
|
|
} // namespace GUI
|
|
|
|
} // namespace Slic3r
|