Files
OrcaSlicer-KX/src/slic3r/GUI/Widgets/ProgressDialog.cpp
chunmao.guo 5e3384f694 ENH: adjust user preset handling
1. async fetch user presets from server (in thread)
2. always load default user presets
3. load user presets from cache immediately
4. not remove user presets cache
5. hide some loading ui

Change-Id: I1d29ed18e09606d0b2f69a69eea2eb5042c26223
2023-01-10 19:08:26 +08:00

882 lines
30 KiB
C++

#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/utils.h"
#include "wx/frame.h"
#include "wx/button.h"
#include "wx/stattext.h"
#include "wx/sizer.h"
#include "wx/event.h"
#include "wx/gauge.h"
#include "wx/intl.h"
#include "wx/dcclient.h"
#include "wx/timer.h"
#include "wx/settings.h"
#include "wx/app.h"
#endif
#include "../GUI.hpp"
#include "../GUI_App.hpp"
#include "../I18N.hpp"
#include "ProgressDialog.hpp"
#include "wx/evtloop.h"
#include "Label.hpp"
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
#define LAYOUT_MARGIN 12
static const int wxID_SKIP = 32000;
namespace Slic3r { namespace GUI {
void ProgressDialog::Init()
{
// we may disappear at any moment, let the others know about it
SetExtraStyle(GetExtraStyle() | wxWS_EX_TRANSIENT);
// Initialize all our members that we always use (even when we don't
// create a valid window in this class).
m_pdStyle = 0;
m_parentTop = NULL;
m_gauge = NULL;
m_msg = NULL;
m_elapsed = m_estimated = m_remaining = NULL;
m_state = Uncancelable;
m_maximum = 0;
m_timeStart = wxGetCurrentTime();
m_timeStop = (unsigned long) -1;
m_break = 0;
m_skip = false;
m_btnAbort = NULL;
m_btnSkip = NULL;
m_display_estimated = m_last_timeupdate = m_ctdelay = 0;
m_delay = 3;
m_winDisabler = NULL;
m_tempEventLoop = NULL;
SetWindowStyle(wxDEFAULT_DIALOG_STYLE);
}
ProgressDialog::ProgressDialog() : wxDialog() { Init(); }
ProgressDialog::ProgressDialog(const wxString &title, const wxString &message, int maximum, wxWindow *parent, int style, bool adaptive) : wxDialog()
{
m_adaptive = adaptive;
Init();
Create(title, message, maximum, parent, style);
Bind(wxEVT_PAINT, &ProgressDialog::OnPaint, this);
Bind(wxEVT_CLOSE_WINDOW, &ProgressDialog::OnClose, this);
}
void ProgressDialog::OnPaint(wxPaintEvent &evt) {}
void ProgressDialog::SetTopParent(wxWindow *parent)
{
m_parent = parent;
m_parentTop = parent;
}
wxString ProgressDialog::FormatString(wxString title)
{
if (!m_adaptive) {
auto current_width = 0;
m_mode = 0;
for (int i = 0; i < title.length(); i++) {
current_width = m_msg->GetTextExtent(title.SubString(0, i)).x;
if (current_width > PROGRESSDIALOG_GAUGE_SIZE.x) {
m_mode = 1;
// title.insert(i - 1, "\n");
break;
}
}
// set mode
if (m_mode == 0) {
m_simplebook->SetSelection(0);
m_msg->SetLabel(title);
} else {
m_simplebook->SetSelection(1);
m_msg_2line->SetLabel(title);
}
} else {
m_msg->SetLabel(title);
m_msg->SetMaxSize(wxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x + 5, -1));
m_msg->SetMinSize(wxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x + 5, -1));
m_msg->Wrap(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x + 5);
m_msg->Layout();
m_msg->Fit();
m_msg_scrolledWindow->SetSize(wxSize(FromDIP(m_msg->GetSize().x + 5), FromDIP(150)));
m_msg_scrolledWindow->SetMinSize(wxSize(FromDIP(m_msg->GetSize().x + 5), FromDIP(150)));
m_msg_scrolledWindow->SetMaxSize(wxSize(FromDIP(m_msg->GetSize().x + 5), FromDIP(150)));
// m_msg_scrolledWindow->Layout();
m_msg_scrolledWindow->Fit();
}
//Fit();
return title;
}
bool ProgressDialog::Create(const wxString &title, const wxString &message, int maximum, wxWindow *parent, int style)
{
SetFont(wxGetApp().normal_font());
SetTopParent(parent);
m_pdStyle = style;
if (!wxDialog::Create(m_parentTop, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, GetWindowStyle())) return false;
SetBackgroundColour(PROGRESSDIALOG_DEF_BK);
/* SetSize(DESIGN_RESOUTION_PROGRESS_SIZE);
SetMinSize(DESIGN_RESOUTION_PROGRESS_SIZE);
SetMaxSize(DESIGN_RESOUTION_PROGRESS_SIZE);*/
SetMaximum(maximum);
EnsureActiveEventLoopExists();
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
if (!HasPDFlag(wxPD_CAN_ABORT)) { EnableCloseButton(false); }
#endif // wxMSW
m_state = HasPDFlag(wxPD_CAN_ABORT) ? Continue : Uncancelable;
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
m_top_line = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0);
m_top_line->SetBackgroundColour(wxColour(166, 169, 170));
m_sizer_main->Add(m_top_line, 0, wxEXPAND, 0);
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(24));
if (!m_adaptive) {
m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, PROGRESSDIALOG_SIMPLEBOOK_SIZE, 0);
m_panel_1line = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, PROGRESSDIALOG_SIMPLEBOOK_SIZE, 0);
m_panel_1line->SetMaxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE);
m_panel_1line->SetBackgroundColour(PROGRESSDIALOG_DEF_BK);
m_simplebook->AddPage(m_panel_1line, wxEmptyString, false);
m_panel_2line = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, PROGRESSDIALOG_SIMPLEBOOK_SIZE, 0);
m_panel_2line->SetMaxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE);
m_panel_2line->SetBackgroundColour(PROGRESSDIALOG_DEF_BK);
m_simplebook->AddPage(m_panel_2line, wxEmptyString, false);
wxBoxSizer *sizer_1line = new wxBoxSizer(wxHORIZONTAL);
m_msg = new wxStaticText(m_panel_1line, wxID_ANY, wxEmptyString, wxDefaultPosition, PROGRESSDIALOG_SIMPLEBOOK_SIZE, 0);
m_msg->Wrap(-1);
m_msg->SetFont(::Label::Body_13);
m_msg->SetForegroundColour(PROGRESSDIALOG_GREY_700);
sizer_1line->Add(m_msg, 0, wxALIGN_CENTER, 0);
m_panel_1line->SetSizer(sizer_1line);
m_panel_1line->Layout();
sizer_1line->Fit(m_panel_1line);
wxBoxSizer *sizer_2line = new wxBoxSizer(wxVERTICAL);
m_msg_2line = new wxStaticText(m_panel_2line, wxID_ANY, wxEmptyString, wxDefaultPosition, PROGRESSDIALOG_SIMPLEBOOK_SIZE, 0);
m_msg_2line->Wrap(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x);
m_msg_2line->SetFont(::Label::Body_13);
m_msg_2line->SetForegroundColour(PROGRESSDIALOG_GREY_700);
m_msg_2line->SetMaxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE);
sizer_2line->Add(m_msg_2line, 1, wxALL, 0);
m_panel_2line->SetSizer(sizer_2line);
m_panel_2line->Layout();
sizer_2line->Fit(m_panel_2line);
m_sizer_main->Add(m_simplebook, 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(28));
} else {
m_msg_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL );
m_msg_scrolledWindow->SetScrollRate(0,5);
wxBoxSizer* m_msg_sizer= new wxBoxSizer(wxVERTICAL);
m_msg = new wxStaticText(m_msg_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x, -1), 0);
m_msg->Wrap(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x);
m_msg->SetFont(::Label::Body_13);
m_msg->SetForegroundColour(PROGRESSDIALOG_GREY_700);
m_msg_sizer->Add(m_msg, 0, wxEXPAND | wxALL, 0);
m_msg_scrolledWindow->SetSizer(m_msg_sizer);
m_msg_scrolledWindow->Layout();
m_msg_sizer->Fit(m_msg_scrolledWindow);
m_sizer_main->Add(m_msg_scrolledWindow, 0, wxEXPAND | wxALL, FromDIP(28));
}
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, 0);
int gauge_style = wxGA_HORIZONTAL;
if (style & wxPD_SMOOTH) gauge_style |= wxGA_SMOOTH;
gauge_style |= wxGA_PROGRESS;
#ifdef __WXMSW__
maximum /= m_factor;
#endif
m_gauge = new wxGauge(this, wxID_ANY, maximum, wxDefaultPosition, PROGRESSDIALOG_GAUGE_SIZE, gauge_style);
m_gauge->SetValue(0);
m_sizer_main->Add(m_gauge, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(28));
#ifdef __WXMSW__
//m_block_left = new wxWindow(m_gauge, wxID_ANY, wxPoint(0, 0), wxSize(FromDIP(2), PROGRESSDIALOG_GAUGE_SIZE.y * 2));
//m_block_left->SetBackgroundColour(PROGRESSDIALOG_DEF_BK);
//m_block_right = new wxWindow(m_gauge, wxID_ANY, wxPoint(PROGRESSDIALOG_GAUGE_SIZE.x - 2, 0), wxSize(FromDIP(2), PROGRESSDIALOG_GAUGE_SIZE.y * 2));
//m_block_right->SetBackgroundColour(PROGRESSDIALOG_DEF_BK);
#endif
wxBoxSizer *m_sizer_bottom = new wxBoxSizer(wxHORIZONTAL);
m_sizer_bottom->Add(0, 0, 1, wxEXPAND, 0);
if (HasPDFlag(wxPD_CAN_ABORT)) {
m_button_cancel = new Button(this, _L("Cancel"));
m_button_cancel->SetTextColor(PROGRESSDIALOG_GREY_700);
m_button_cancel->SetMinSize(PROGRESSDIALOG_CANCEL_BUTTON_SIZE);
m_button_cancel->SetCornerRadius(PROGRESSDIALOG_CANCEL_BUTTON_SIZE.y / 2);
m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) {
if (m_state == Finished) {
event.Skip();
} else {
m_state = Canceled;
DisableAbort();
m_button_cancel->Enable(false);
DisableSkip();
m_timeStop = wxGetCurrentTime();
}
});
m_sizer_bottom->Add(m_button_cancel, 0, wxALL, 0);
}
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(16));
m_sizer_main->Add(m_sizer_bottom, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(28));
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(10));
wxGetApp().UpdateDlgDarkUI(this);
SetSizer(m_sizer_main);
Layout();
Fit();
Centre(wxCENTER_FRAME | wxBOTH);
DisableOtherWindows();
FormatString(message);
Show();
Enable();
if (m_elapsed) { SetTimeLabel(0, m_elapsed); }
Update();
return true;
//
// sizerTop = new wxBoxSizer(wxVERTICAL);
// auto def_size_width = wxMin(wxGetClientDisplayRect().width / 3, 305);
//
// wxStaticLine *m_staticline2 = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxSize(def_size_width, 1), wxLI_HORIZONTAL);
// sizerTop->Add(m_staticline2, 0, wxEXPAND, 0);
// auto m_block_top = new wxWindow(this, wxID_ANY, wxDefaultPosition, wxSize(def_size_width, 20));
// m_block_top->SetBackgroundColour(wxColor(DESIGN_RESOUTION_DEF_BK_COLOR));
// sizerTop->Add(m_block_top, 0, wxEXPAND);
//
// m_message_area = new wxWindow(this, wxID_ANY, wxDefaultPosition, wxSize(def_size_width, 38));
// m_message_area->SetBackgroundColour(DESIGN_RESOUTION_DEF_BK_COLOR);
// sizerTop->Add(m_message_area, 0, wxLEFT | wxRIGHT | wxBottom | wxEXPAND, 2 * LAYOUT_MARGIN);
//
// m_msg = new wxStaticText(m_message_area, wxID_ANY, wxString(""), wxPoint(0, 10), wxSize(def_size_width, 20), wxST_ELLIPSIZE_END);
// m_msg->SetForegroundColour(wxColour(107, 107, 107));
// m_msg->SetBackgroundColour(DESIGN_RESOUTION_DEF_BK_COLOR);
// m_msg->SetMinSize(wxSize(def_size_width, 20));
// m_msg->SetMaxSize(wxSize(def_size_width, 20));
//
// m_msg_2line = new wxStaticText(m_message_area, wxID_ANY, wxString(""), wxPoint(0, 0), wxSize(def_size_width, 40), wxST_ELLIPSIZE_END);
// m_msg_2line->SetForegroundColour(wxColour(107, 107, 107));
// m_msg_2line->SetBackgroundColour(DESIGN_RESOUTION_DEF_BK_COLOR);
// m_msg_2line->SetMinSize(wxSize(def_size_width, 40));
// m_msg_2line->SetMaxSize(wxSize(def_size_width, 40));
//
// auto block_left = new wxWindow(m_gauge, -1, wxPoint(0, 0), wxSize(2, m_gauge->GetSize().GetHeight()));
// block_left->SetBackgroundColour(wxColor(255, 255, 255));
//
// auto block_right = new wxWindow(m_gauge, -1, wxPoint(m_gauge->GetSize().GetWidth() - 2, 0), wxSize(2, m_gauge->GetSize().GetHeight()));
// block_right->SetBackgroundColour(wxColor(255, 255, 255));
//
// sizerTop->Add(m_gauge, 0, wxLEFT | wxRIGHT | wxBottom, 2 * LAYOUT_MARGIN);
// m_gauge->SetValue(0);
//
// m_elapsed = m_estimated = m_remaining = NULL;
// size_t nTimeLabels = 0;
//
// wxSizer *const sizerLabels = new wxFlexGridSizer(2);
//
// if (style & wxPD_ELAPSED_TIME) {
// nTimeLabels++;
//
// m_elapsed = CreateLabel(GetElapsedLabel(), sizerLabels);
// }
//
// if (style & wxPD_ESTIMATED_TIME) {
// nTimeLabels++;
//
// m_estimated = CreateLabel(GetEstimatedLabel(), sizerLabels);
// }
//
// if (style & wxPD_REMAINING_TIME) {
// nTimeLabels++;
//
// m_remaining = CreateLabel(GetRemainingLabel(), sizerLabels);
// }
// sizerTop->Add(sizerLabels, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxBottom, LAYOUT_MARGIN);
//
// wxStdDialogButtonSizer *buttonSizer = wxDialog::CreateStdDialogButtonSizer(0);
//
// const int borderFlags = wxALL;
//
// std::string icon_path = (boost::format("%1%/images/common_dialog_confirm.png") % resources_dir()).str();
// /*m_btnAbort = new wxBitmapButton(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(52, 24), wxBU_AUTODRAW | wxBORDER_NONE);
// m_btnAbort->SetBitmap(wxBitmap(icon_path, wxBITMAP_TYPE_ANY));
// m_btnAbort->SetBitmapDisabled(wxBitmap(icon_path, wxBITMAP_TYPE_ANY));
// m_btnAbort->SetBitmapPressed(wxBitmap(icon_path, wxBITMAP_TYPE_ANY));
// m_btnAbort->SetBitmapFocus(wxBitmap(icon_path, wxBITMAP_TYPE_ANY));
// m_btnAbort->SetBitmapCurrent(wxBitmap(icon_path, wxBITMAP_TYPE_ANY));*/
//
// m_btnAbort = new wxButton(this, wxID_CANCEL, wxString(""), wxDefaultPosition, wxSize(52,24));
//
// wxStaticBitmap *m_bitmatAbort = new wxStaticBitmap(m_btnAbort, wxID_ANY, wxBitmap(icon_path, wxBITMAP_TYPE_ANY), wxDefaultPosition, wxSize(52, 24), 0);
// wxStaticText *textAbort = new wxStaticText(m_btnAbort, wxID_ANY, _T("Cancel"), wxPoint(5, 3), wxSize(42, 19));
// textAbort->SetBa
// ckgroundColour(wxColor(0, 174, 66));
// textAbort->SetForegroundColour(DESIGN_RESOUTION_DEF_BK_COLOR);
//
// textAbort->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) {
// if (m_state == Finished) {
// event.Skip();
// } else {
// m_state = Canceled;
// //DisableAbort();
// DisableSkip();
// m_timeStop = wxGetCurrentTime();
// }
// });
//
// wxSizerFlags sizerFlags = wxSizerFlags().Border(borderFlags, LAYOUT_MARGIN);
// wxWindow *m_button_sizer = new wxWindow(this, wxID_ANY, wxDefaultPosition, wxSize(def_size_width, 26));
// m_button_sizer->SetBackgroundColour(DESIGN_RESOUTION_DEF_BK_COLOR);
//
// if (HasPDFlag(wxPD_CAN_SKIP)) {
// m_btnSkip = new wxButton(this, wxID_SKIP, wxGetTranslation("&Skip"));
// m_btnSkip->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &ProgressDialog::OnSkip, this);
// buttonSizer->SetNegativeButton(m_btnSkip);
// }
//
// if (HasPDFlag(wxPD_CAN_ABORT)) {
// /*m_btnAbort = new wxButton(this, wxID_CANCEL);
// buttonSizer->SetCancelButton(m_btnAbort);
// m_btnAbort->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &ProgressDialog::OnCancel, this);*/
//
// m_button_calcel = new Button(m_button_sizer, _T("Cancel"));
// m_button_calcel->SetTextColor(wxColour(107, 107, 107));
// m_button_calcel->SetSize(60, 24);
// m_button_calcel->SetPosition(wxPoint(m_button_sizer->GetSize().GetWidth() - m_button_calcel->GetSize().GetWidth(), 0));
//
// m_button_calcel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) {
// if (m_state == Finished) {
// event.Skip();
// } else {
// m_state = Canceled;
// DisableAbort();
// m_button_calcel->Enable(false);
// DisableSkip();
// m_timeStop = wxGetCurrentTime();
// }
// });
// }
//
// if (!HasPDFlag(wxPD_CAN_SKIP | wxPD_CAN_ABORT)) buttonSizer->AddSpacer(LAYOUT_MARGIN);
//
// sizerTop->Add(m_button_sizer, 1, wxEXPAND, 0);
//
// auto m_block_bottom = new wxWindow(this, wxID_ANY, wxDefaultPosition, wxSize(def_size_width, 15));
// m_block_bottom->SetBackgroundColour(DESIGN_RESOUTION_DEF_BK_COLOR);
// sizerTop->Add(m_block_bottom, 0, wxEXPAND);
//
// SetSizerAndFit(sizerTop);
// FormatString(message);
//
// Centre(wxCENTER_FRAME | wxBOTH);
//
// DisableOtherWindows();
//
// Show();
// Enable();
// if (m_elapsed) { SetTimeLabel(0, m_elapsed); }
//
// Update();
// return true;
}
void ProgressDialog::UpdateTimeEstimates(int value, unsigned long &elapsedTime, unsigned long &estimatedTime, unsigned long &remainingTime)
{
unsigned long elapsed = wxGetCurrentTime() - m_timeStart;
if (value != 0 && (m_last_timeupdate < elapsed || value == m_maximum)) {
m_last_timeupdate = elapsed;
unsigned long estimated = m_break + (unsigned long) (((double) (elapsed - m_break) * m_maximum) / ((double) value));
if (estimated > m_display_estimated && m_ctdelay >= 0) {
++m_ctdelay;
} else if (estimated < m_display_estimated && m_ctdelay <= 0) {
--m_ctdelay;
} else {
m_ctdelay = 0;
}
if (m_ctdelay >= m_delay // enough confirmations for a higher value
|| m_ctdelay <= (m_delay * -1) // enough confirmations for a lower value
|| value == m_maximum // to stay consistent
|| elapsed > m_display_estimated // to stay consistent
|| (elapsed > 0 && elapsed < 4) // additional updates in the beginning
) {
m_display_estimated = estimated;
m_ctdelay = 0;
}
}
if (value != 0) {
long display_remaining = m_display_estimated - elapsed;
if (display_remaining < 0) { display_remaining = 0; }
estimatedTime = m_display_estimated;
remainingTime = display_remaining;
}
elapsedTime = elapsed;
}
// static
wxString ProgressDialog::GetFormattedTime(unsigned long timeInSec)
{
wxString timeAsHMS;
if (timeInSec == (unsigned long) -1) {
timeAsHMS = wxGetTranslation("Unknown");
} else {
unsigned hours = timeInSec / 3600;
unsigned minutes = (timeInSec % 3600) / 60;
unsigned seconds = timeInSec % 60;
timeAsHMS.Printf("%u:%02u:%02u", hours, minutes, seconds);
}
return timeAsHMS;
}
void ProgressDialog::EnsureActiveEventLoopExists()
{
if (!wxEventLoopBase::GetActive()) {
m_tempEventLoop = new wxEventLoop;
wxEventLoop::SetActive(m_tempEventLoop);
}
}
wxStaticText *ProgressDialog::CreateLabel(const wxString &text, wxSizer *sizer)
{
wxStaticText *label = new wxStaticText(this, wxID_ANY, text);
wxStaticText *value = new wxStaticText(this, wxID_ANY, wxGetTranslation("unknown"));
// select placement most native or nice on target GUI
#if defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXGTK20__)
// value and time centered in one row
sizer->Add(label, 1, wxALIGN_RIGHT | wxTOP | wxRIGHT, LAYOUT_MARGIN);
sizer->Add(value, 1, wxALIGN_LEFT | wxTOP, LAYOUT_MARGIN);
#else
// value and time to the right in one row
sizer->Add(label);
sizer->Add(value, 0, wxLEFT, LAYOUT_MARGIN);
#endif
return value;
}
// ----------------------------------------------------------------------------
// ProgressDialog operations
// ----------------------------------------------------------------------------
bool ProgressDialog::Update(int value, const wxString &newmsg, bool *skip)
{
if (!DoBeforeUpdate(skip)) return false;
wxCHECK_MSG(m_gauge, false, "dialog should be fully created");
#ifdef __WXMSW__
value /= m_factor;
#endif // __WXMSW__
wxASSERT_MSG(value <= m_maximum, wxT("invalid progress value"));
m_gauge->SetValue(value);
UpdateMessage(newmsg);
if ((m_elapsed || m_remaining || m_estimated) && (value != 0)) {
unsigned long elapsed;
unsigned long display_remaining;
UpdateTimeEstimates(value, elapsed, m_display_estimated, display_remaining);
SetTimeLabel(elapsed, m_elapsed);
SetTimeLabel(m_display_estimated, m_estimated);
SetTimeLabel(display_remaining, m_remaining);
}
if (value == m_maximum) {
if (m_state == Finished) {
// ignore multiple calls to Update(m_maximum): it may sometimes be
// troublesome to ensure that Update() is not called twice with the
// same value (e.g. because of the rounding errors) and if we don't
// return now we're going to generate asserts below
return true;
}
// so that we return true below and that out [Cancel] handler knew what
// to do
m_state = Finished;
if (!HasPDFlag(wxPD_AUTO_HIDE)) {
EnableClose();
DisableSkip();
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
EnableCloseButton();
#endif // __WXMSW__
if (newmsg.empty()) {
// also provide the finishing message if the application didn't
m_msg->SetLabel(wxGetTranslation("Done."));
}
// allow the window to repaint:
// NOTE: since we yield only for UI events with this call, there
// should be no side-effects
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
// NOTE: this call results in a new event loop being created
// and to a call to ProcessPendingEvents() (which may generate
// unwanted re-entrancies).
(void) ShowModal();
} else // auto hide
{
// reenable other windows before hiding this one because otherwise
// Windows wouldn't give the focus back to the window which had
// been previously focused because it would still be disabled
ReenableOtherWindows();
Hide();
}
} else // not at maximum yet
{
DoAfterUpdate();
}
// update the display in case yielding above didn't do it
Update();
return m_state != Canceled;
}
bool ProgressDialog::Pulse(const wxString &newmsg, bool *skip)
{
if (!DoBeforeUpdate(skip)) return false;
wxCHECK_MSG(m_gauge, false, "dialog should be fully created");
// show a bit of progress
m_gauge->Pulse();
UpdateMessage(newmsg);
if (m_elapsed || m_remaining || m_estimated) {
unsigned long elapsed = wxGetCurrentTime() - m_timeStart;
SetTimeLabel(elapsed, m_elapsed);
SetTimeLabel((unsigned long) -1, m_estimated);
SetTimeLabel((unsigned long) -1, m_remaining);
}
DoAfterUpdate();
return m_state != Canceled;
}
bool ProgressDialog::WasCanceled() const { return m_state == Canceled; }
bool ProgressDialog::DoBeforeUpdate(bool *skip)
{
// we have to yield because not only we want to update the display but
// also to process the clicks on the cancel and skip buttons
// NOTE: using YieldFor() this call shouldn't give re-entrancy problems
// for event handlers not interested to UI/user-input events.
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
Update();
if (m_skip && skip && !*skip) {
*skip = true;
m_skip = false;
EnableSkip();
}
return m_state != Canceled;
}
void ProgressDialog::DoAfterUpdate()
{
// allow the window to repaint:
// NOTE: since we yield only for UI events with this call, there
// should be no side-effects
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
}
void ProgressDialog::Resume()
{
m_state = Continue;
m_ctdelay = m_delay; // force an update of the elapsed/estimated/remaining time
m_break += wxGetCurrentTime() - m_timeStop;
EnableAbort();
m_button_cancel->Enable(true);
EnableSkip();
m_skip = false;
}
bool ProgressDialog::Show(bool show)
{
// reenable other windows before hiding this one because otherwise
// Windows wouldn't give the focus back to the window which had
// been previously focused because it would still be disabled
if (!show) ReenableOtherWindows();
return wxDialog::Show(show);
}
int ProgressDialog::GetValue() const
{
wxCHECK_MSG(m_gauge, -1, "dialog should be fully created");
return m_gauge->GetValue();
}
int ProgressDialog::GetRange() const { return m_maximum; }
wxString ProgressDialog::GetMessage() const { return m_msg->GetLabel(); }
void ProgressDialog::SetRange(int maximum)
{
wxCHECK_RET(m_gauge, "dialog should be fully created");
wxCHECK_RET(maximum > 0, "Invalid range");
m_gauge->SetRange(maximum);
SetMaximum(maximum);
}
void ProgressDialog::SetMaximum(int maximum)
{
m_maximum = maximum;
#if defined(__WXMSW__)
// we can't have values > 65,536 in the progress control under Windows, so
// scale everything down
m_factor = m_maximum / 65536 + 1;
m_maximum /= m_factor;
#endif // __WXMSW__
}
bool ProgressDialog::WasCancelled() const { return HasPDFlag(wxPD_CAN_ABORT) && m_state == Canceled; }
bool ProgressDialog::WasSkipped() const { return HasPDFlag(wxPD_CAN_SKIP) && m_skip; }
// static
void ProgressDialog::SetTimeLabel(unsigned long val, wxStaticText *label)
{
if (label) {
wxString s;
if (val != (unsigned long) -1) {
s = GetFormattedTime(val);
} else {
s = wxGetTranslation("Unknown");
}
if (s != label->GetLabel()) label->SetLabel(s);
}
}
// ----------------------------------------------------------------------------
// event handlers
// ----------------------------------------------------------------------------
void ProgressDialog::OnCancel(wxCommandEvent &event)
{
if (m_state == Finished) {
// this means that the count down is already finished and we're being
// shown as a modal dialog - so just let the default handler do the job
event.Skip();
} else {
// request to cancel was received, the next time Update() is called we
// will handle it
m_state = Canceled;
// update the buttons state immediately so that the user knows that the
// request has been noticed
DisableAbort();
DisableSkip();
// save the time when the dialog was stopped
m_timeStop = wxGetCurrentTime();
}
}
void ProgressDialog::OnSkip(wxCommandEvent &WXUNUSED(event))
{
DisableSkip();
m_skip = true;
}
void ProgressDialog::OnClose(wxCloseEvent &event)
{
if (m_state == Uncancelable) {
// can't close this dialog
event.Veto();
} else if (m_state == Finished) {
// let the default handler close the window as we already terminated
event.Skip();
} else {
// next Update() will notice it
m_state = Canceled;
DisableAbort();
DisableSkip();
m_timeStop = wxGetCurrentTime();
}
}
// ----------------------------------------------------------------------------
// destruction
// ----------------------------------------------------------------------------
ProgressDialog::~ProgressDialog()
{
// normally this should have been already done, but just in case
ReenableOtherWindows();
if (m_tempEventLoop) {
// If another event loop has been installed as active during the life
// time of this object, we shouldn't deactivate it, but we also can't
// delete our m_tempEventLoop in this case because it risks leaving the
// new event loop with a dangling pointer, which it will set back as
// the active loop when it exits, resulting in a crash. So we have no
// choice but to just leak this pointer then, which is, of course, bad
// and usually easily avoidable by just destroying the progress dialog
// sooner, so warn the programmer about it.
wxCHECK_RET(wxEventLoopBase::GetActive() == m_tempEventLoop, "current event loop must not be changed during "
"ProgressDialog lifetime");
wxEventLoopBase::SetActive(NULL);
delete m_tempEventLoop;
}
}
void ProgressDialog::DoSetSize(int x, int y, int width, int height, int sizeFlags /*= wxSIZE_AUTO*/)
{
if (m_button_cancel != nullptr) { m_button_cancel->SetMinSize(PROGRESSDIALOG_CANCEL_BUTTON_SIZE); }
#ifdef __WXMSW__
//if (m_block_left != nullptr && m_block_right != nullptr) {
// m_block_left->SetPosition(wxPoint(0, 0));
// m_block_right->SetPosition(wxPoint(PROGRESSDIALOG_GAUGE_SIZE.x - 2, 0));
//}
#endif
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
}
void ProgressDialog::DisableOtherWindows()
{
if (HasPDFlag(wxPD_APP_MODAL)) {
#if defined(__WXOSX__)
if (m_parentTop) m_parentTop->Disable();
m_winDisabler = NULL;
#else
m_winDisabler = new wxWindowDisabler(this);
#endif
} else {
if (m_parentTop) m_parentTop->Disable();
m_winDisabler = NULL;
}
}
void ProgressDialog::ReenableOtherWindows()
{
if (HasPDFlag(wxPD_APP_MODAL)) {
#if defined(__WXOSX__)
if (m_parentTop) m_parentTop->Enable();
#else
wxDELETE(m_winDisabler);
#endif
} else {
if (m_parentTop) m_parentTop->Enable();
}
}
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
void ProgressDialog::EnableSkip(bool enable)
{
if (HasPDFlag(wxPD_CAN_SKIP)) {
if (m_btnSkip) m_btnSkip->Enable(enable);
}
}
void ProgressDialog::EnableAbort(bool enable)
{
if (HasPDFlag(wxPD_CAN_ABORT)) {
if (m_btnAbort) m_btnAbort->Enable(enable);
}
}
void ProgressDialog::EnableClose()
{
if (HasPDFlag(wxPD_CAN_ABORT)) {
if (m_btnAbort) {
m_btnAbort->Enable();
m_btnAbort->SetLabel(wxGetTranslation("Close"));
}
}
}
void ProgressDialog::UpdateMessage(const wxString &newmsg)
{
if (!newmsg.empty() && newmsg != m_msg->GetLabel()) {
const wxSize sizeOld = m_msg->GetSize();
FormatString(newmsg);
Fit();
// m_msg->SetLabel(newmsg);
if (m_msg->GetSize().x > sizeOld.x) {
// Resize the dialog to fit its new, longer contents instead of
// just truncating it.
Fit();
}
// allow the window to repaint:
// NOTE: since we yield only for UI events with this call, there
// should be no side-effects
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
}
}
}} // namespace Slic3r::GUI