From f60a767ed9ce029f93702c7341a632f5c31a703b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 12 Dec 2018 14:40:56 +0100 Subject: [PATCH 1/5] Fix of the wipe tower manipulation: 3D scene should maintain selection status of the wipe tower after the wipe tower is updated. --- src/slic3r/GUI/GLCanvas3D.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cc86b9b7a..74a472464 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4094,7 +4094,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_reload_delayed = ! m_canvas->IsShown() && ! refresh_immediately && ! force_full_scene_refresh; - PrinterTechnology printer_technology = m_process->current_printer_technology(); + PrinterTechnology printer_technology = m_process->current_printer_technology(); + int volume_idx_wipe_tower_old = -1; if (m_regenerate_volumes) { @@ -4152,6 +4153,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } if (mvs == nullptr || force_full_scene_refresh) { // This GLVolume will be released. + if (volume->is_wipe_tower) { + // There is only one wipe tower. + assert(volume_idx_wipe_tower_old == -1); + volume_idx_wipe_tower_old = (int)volume_id; + } volume->release_geometry(); if (! m_reload_delayed) delete volume; @@ -4319,8 +4325,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re float depth = print->get_wipe_tower_depth(); if (!print->is_step_done(psWipeTower)) depth = (900.f/w) * (float)(extruders_count - 1) ; - m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower), - print->config().nozzle_diameter.values[0] * 1.25f * 4.5f); + int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( + 1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower), + print->config().nozzle_diameter.values[0] * 1.25f * 4.5f); + if (volume_idx_wipe_tower_old != -1) + map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; } } From e1e4bf74ba98e261616a1fa09466cf6a992e85c7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 Dec 2018 13:18:38 +0100 Subject: [PATCH 2/5] ObjectList: first column editing --- src/slic3r/GUI/GUI_ObjectList.cpp | 17 ++++++--------- src/slic3r/GUI/wxExtensions.cpp | 36 +++++++++++++++++++++++++++++++ src/slic3r/GUI/wxExtensions.hpp | 10 +++++++-- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 22ebc093f..bf7e7255a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -337,9 +337,7 @@ void ObjectList::selection_changed() void ObjectList::OnChar(wxKeyEvent& event) { -// printf("KeyDown event\n"); if (event.GetKeyCode() == WXK_BACK){ - printf("WXK_BACK\n"); remove(); } else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_SHIFT)) @@ -370,15 +368,14 @@ void ObjectList::OnContextMenu(wxDataViewEvent&) if (title == " ") show_context_menu(); - - else if (title == _("Name") && pt.x >15 && - m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData()) - { - if (is_windows10()) { - const auto obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); - wxGetApp().plater()->fix_through_netfabb(obj_idx); - } + else if (title == _("Name") && pt.x >15 && + m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData()) + { + if (is_windows10()) { + const auto obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); + wxGetApp().plater()->fix_through_netfabb(obj_idx); } + } #ifndef __WXMSW__ GetMainWindow()->SetToolTip(""); // hide tooltip #endif //__WXMSW__ diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 57828ea9a..8d94c964f 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1281,6 +1281,42 @@ wxSize PrusaBitmapTextRenderer::GetSize() const } +wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +{ + wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); + PrusaObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); + + if ( !(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject)) ) + return nullptr; + + PrusaDataViewBitmapText data; + data << value; + m_bmp_from_editing_item = data.GetBitmap(); + + wxPoint position = labelRect.GetPosition(); + if (m_bmp_from_editing_item.IsOk()) { + const int bmp_width = m_bmp_from_editing_item.GetWidth(); + position.x += bmp_width; + labelRect.SetWidth(labelRect.GetWidth() - bmp_width); + } + + wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(), + position, labelRect.GetSize(), wxTE_PROCESS_ENTER); + text_editor->SetInsertionPointEnd(); + + return text_editor; +} + +bool PrusaBitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +{ + wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl); + if (!text_editor) + return false; + + value << PrusaDataViewBitmapText(text_editor->GetValue(), m_bmp_from_editing_item); + return true; +} + // ---------------------------------------------------------------------------- // PrusaDoubleSlider // ---------------------------------------------------------------------------- diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 866317e25..97f753906 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -522,7 +522,7 @@ public: class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer { public: - PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, + PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE, int align = wxDVR_DEFAULT_ALIGNMENT): wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {} @@ -532,10 +532,16 @@ public: virtual bool Render(wxRect cell, wxDC *dc, int state); virtual wxSize GetSize() const; - virtual bool HasEditorCtrl() const { return false; } + bool HasEditorCtrl() const override { return true; } + wxWindow* CreateEditorCtrl(wxWindow* parent, + wxRect labelRect, + const wxVariant& value) override; + bool GetValueFromEditorCtrl( wxWindow* ctrl, + wxVariant& value) override; private: PrusaDataViewBitmapText m_value; + wxBitmap m_bmp_from_editing_item; }; From 416f220c362d5f31803e33535f69b06c1875880f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 Dec 2018 14:35:18 +0100 Subject: [PATCH 3/5] Save edited object/part's name to the object model --- src/slic3r/GUI/GUI_ObjectList.cpp | 46 ++++++++++++++++++++++++++----- src/slic3r/GUI/GUI_ObjectList.hpp | 3 ++ src/slic3r/GUI/wxExtensions.cpp | 12 +++++++- src/slic3r/GUI/wxExtensions.hpp | 2 ++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index bf7e7255a..7ed6e5a28 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -75,6 +75,8 @@ ObjectList::ObjectList(wxWindow* parent) : Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, &ObjectList::OnDropPossible, this); Bind(wxEVT_DATAVIEW_ITEM_DROP, &ObjectList::OnDrop, this); + Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &ObjectList::OnEditingDone, this); + Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &ObjectList::ItemValueChanged, this); Bind(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, [this](wxCommandEvent& e) { last_volume_is_deleted(e.GetInt()); }); @@ -290,6 +292,21 @@ void ObjectList::update_extruder_in_config(const wxDataViewItem& item) wxGetApp().plater()->update(); } +void ObjectList::update_name_in_model(const wxDataViewItem& item) +{ + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx < 0) return; + + if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { + (*m_objects)[obj_idx]->name = m_objects_model->GetName(item).ToStdString(); + return; + } + + const int volume_id = m_objects_model->GetVolumeIdByItem(item); + if (volume_id < 0) return; + (*m_objects)[obj_idx]->volumes[volume_id]->name = m_objects_model->GetName(item).ToStdString(); +} + void ObjectList::init_icons() { m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); @@ -452,10 +469,10 @@ void ObjectList::OnDropPossible(wxDataViewEvent &event) wxDataViewItem item(event.GetItem()); // only allow drags for item or background, not containers - if (item.IsOk() && - (m_objects_model->GetParent(item) == wxDataViewItem(0) || + if (!item.IsOk() || + m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->GetItemType(item) != itVolume || - m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item))) + m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) event.Veto(); } @@ -463,9 +480,9 @@ void ObjectList::OnDrop(wxDataViewEvent &event) { wxDataViewItem item(event.GetItem()); - if (item.IsOk() && ( m_objects_model->GetParent(item) == wxDataViewItem(0) || - m_objects_model->GetItemType(item) != itVolume) || - m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) { + if (!item.IsOk() || m_objects_model->GetParent(item) == wxDataViewItem(0) || + m_objects_model->GetItemType(item) != itVolume || + m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) { event.Veto(); m_dragged_data.clear(); return; @@ -1664,7 +1681,22 @@ void ObjectList::update_settings_items() void ObjectList::ItemValueChanged(wxDataViewEvent &event) { - update_extruder_in_config(event.GetItem()); + if (event.GetColumn() == 0) + update_name_in_model(event.GetItem()); + else if (event.GetColumn() == 1) + update_extruder_in_config(event.GetItem()); +} + +void ObjectList::OnEditingDone(wxDataViewEvent &event) +{ + if (event.GetColumn() != 0) + return; + + const auto renderer = dynamic_cast(GetColumn(0)->GetRenderer()); + + if (renderer->WasCanceled()) + show_error(this, _(L("The supplied name is not valid;")) + "\n" + + _(L("the following characters are not allowed:")) + " <>:/\\|?*\""); } } //namespace GUI diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 33e3894fe..3664e6fda 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -128,6 +128,8 @@ public: void set_extruder_column_hidden(const bool hide) const; // update extruder in current config void update_extruder_in_config(const wxDataViewItem& item); + // update changed name in the object model + void update_name_in_model(const wxDataViewItem& item); void update_extruder_values_for_items(const int max_extruder); void init_icons(); @@ -227,6 +229,7 @@ private: void OnDrop(wxDataViewEvent &event); void ItemValueChanged(wxDataViewEvent &event); + void OnEditingDone(wxDataViewEvent &event); }; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 8d94c964f..2e5a37040 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1292,6 +1292,7 @@ wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect lab PrusaDataViewBitmapText data; data << value; m_bmp_from_editing_item = data.GetBitmap(); + m_was_unusable_symbol = false; wxPoint position = labelRect.GetPosition(); if (m_bmp_from_editing_item.IsOk()) { @@ -1310,9 +1311,18 @@ wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect lab bool PrusaBitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) { wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl); - if (!text_editor) + if (!text_editor || text_editor->GetValue().IsEmpty()) return false; + std::string chosen_name = Slic3r::normalize_utf8_nfc(text_editor->GetValue().ToUTF8()); + const char* unusable_symbols = "<>:/\\|?*\""; + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { + m_was_unusable_symbol = true; + return false; + } + } + value << PrusaDataViewBitmapText(text_editor->GetValue(), m_bmp_from_editing_item); return true; } diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 97f753906..3fbf9a083 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -538,10 +538,12 @@ public: const wxVariant& value) override; bool GetValueFromEditorCtrl( wxWindow* ctrl, wxVariant& value) override; + bool WasCanceled() const { return m_was_unusable_symbol; } private: PrusaDataViewBitmapText m_value; wxBitmap m_bmp_from_editing_item; + bool m_was_unusable_symbol; }; From b8939ed7df4b4e47d0323d806cbde6a7679ef471 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 12 Dec 2018 15:09:20 +0100 Subject: [PATCH 4/5] Print time in output G-code: fill in the time if the file export dialog is open after the slicing finished. --- src/libslic3r/Print.cpp | 48 +++++++++++++++++++-- src/libslic3r/Print.hpp | 7 +++ src/slic3r/GUI/BackgroundSlicingProcess.cpp | 27 +----------- 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 3bbe00805..4d509f112 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -14,6 +14,8 @@ #include "PrintExport.hpp" +#include + //! macro used to mark string used at localization, //! return same string #define L(s) Slic3r::I18N::translate(s) @@ -1864,14 +1866,54 @@ std::string Print::output_filename() const { // Set the placeholders for the data know first after the G-code export is finished. // These values will be just propagated into the output file name. + DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); + return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode", &config); +} + +DynamicConfig PrintStatistics::config() const +{ + DynamicConfig config; + std::string normal_print_time = this->estimated_normal_print_time; + std::string silent_print_time = this->estimated_silent_print_time; + normal_print_time.erase(std::remove_if(normal_print_time.begin(), normal_print_time.end(), std::isspace), normal_print_time.end()); + silent_print_time.erase(std::remove_if(silent_print_time.begin(), silent_print_time.end(), std::isspace), silent_print_time.end()); + config.set_key_value("print_time", new ConfigOptionString(normal_print_time)); + config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time)); + config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time)); + config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament)); + config.set_key_value("extruded_volume", new ConfigOptionFloat (this->total_extruded_volume)); + config.set_key_value("total_cost", new ConfigOptionFloat (this->total_cost)); + config.set_key_value("total_weight", new ConfigOptionFloat (this->total_weight)); + config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat (this->total_wipe_tower_cost)); + config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat (this->total_wipe_tower_filament)); + return config; +} + +DynamicConfig PrintStatistics::placeholders() +{ DynamicConfig config; for (const std::string &key : { "print_time", "normal_print_time", "silent_print_time", "used_filament", "extruded_volume", "total_cost", "total_weight", "total_wipe_tower_cost", "total_wipe_tower_filament"}) - config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); - return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode", &config); + config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); + return config; +} + +std::string PrintStatistics::finalize_output_path(const std::string &path_in) const +{ + std::string final_path; + try { + boost::filesystem::path path(path_in); + DynamicConfig cfg = this->config(); + PlaceholderParser pp; + std::string new_stem = pp.process(path.stem().string(), 0, &cfg); + final_path = (path.parent_path() / (new_stem + path.extension().string())).string(); + } catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what(); + final_path = path_in; + } + return final_path; } } // namespace Slic3r - diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 27ee6f3c2..1b79ef295 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -249,6 +249,13 @@ struct PrintStatistics double total_wipe_tower_filament; std::map filament_stats; + // Config with the filled in print statistics. + DynamicConfig config() const; + // Config with the statistics keys populated with placeholder strings. + static DynamicConfig placeholders(); + // Replace the print statistics placeholders in the path. + std::string finalize_output_path(const std::string &path_in) const; + void clear() { estimated_normal_print_time.clear(); estimated_silent_print_time.clear(); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index b31fa6b5f..b9f146013 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -82,32 +82,7 @@ void BackgroundSlicingProcess::process_fff() if (! m_export_path.empty()) { //FIXME localize the messages // Perform the final post-processing of the export path by applying the print statistics over the file name. - std::string export_path; - { - const PrintStatistics &stats = m_fff_print->print_statistics(); - PlaceholderParser pp; - std::string normal_print_time = stats.estimated_normal_print_time; - std::string silent_print_time = stats.estimated_silent_print_time; - normal_print_time.erase(std::remove_if(normal_print_time.begin(), normal_print_time.end(), isspace), normal_print_time.end()); - silent_print_time.erase(std::remove_if(silent_print_time.begin(), silent_print_time.end(), isspace), silent_print_time.end()); - pp.set("print_time", new ConfigOptionString(normal_print_time)); - pp.set("normal_print_time", new ConfigOptionString(normal_print_time)); - pp.set("silent_print_time", new ConfigOptionString(silent_print_time)); - pp.set("used_filament", new ConfigOptionFloat (stats.total_used_filament)); - pp.set("extruded_volume", new ConfigOptionFloat (stats.total_extruded_volume)); - pp.set("total_cost", new ConfigOptionFloat (stats.total_cost)); - pp.set("total_weight", new ConfigOptionFloat (stats.total_weight)); - pp.set("total_wipe_tower_cost", new ConfigOptionFloat (stats.total_wipe_tower_cost)); - pp.set("total_wipe_tower_filament", new ConfigOptionFloat (stats.total_wipe_tower_filament)); - boost::filesystem::path path(m_export_path); - try { - std::string new_stem = pp.process(path.stem().string(), 0); - export_path = (path.parent_path() / (new_stem + path.extension().string())).string(); - } catch (const std::exception &ex) { - BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what(); - export_path = m_export_path; - } - } + std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); if (copy_file(m_temp_output_path, export_path) != 0) throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); m_print->set_status(95, "Running post-processing scripts"); From 598e6f648bb9173df0c1a3ee3ed2f3437d577806 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 12 Dec 2018 15:48:39 +0100 Subject: [PATCH 5/5] Time to print into the G-code file name: Round it to full minutes. --- src/libslic3r/Print.cpp | 46 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4d509f112..4858f5886 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1870,13 +1870,51 @@ std::string Print::output_filename() const return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode", &config); } +// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes +// and removing spaces. +static std::string short_time(const std::string &time) +{ + // Parse the dhms time format. + int days = 0; + int hours = 0; + int minutes = 0; + int seconds = 0; + if (time.find('d') != std::string::npos) + ::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds); + else if (time.find('h') != std::string::npos) + ::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds); + else if (time.find('m') != std::string::npos) + ::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds); + else if (time.find('s') != std::string::npos) + ::sscanf(time.c_str(), "%ds", &seconds); + // Round to full minutes. + if (days + hours + minutes > 0 && seconds >= 30) { + if (++ minutes == 60) { + minutes = 0; + if (++ hours == 24) { + hours = 0; + ++ days; + } + } + } + // Format the dhm time. + char buffer[64]; + if (days > 0) + ::sprintf(buffer, "%dd%dh%dm", days, hours, minutes); + else if (hours > 0) + ::sprintf(buffer, "%dh%dm", hours, minutes); + else if (minutes > 0) + ::sprintf(buffer, "%dm", minutes); + else + ::sprintf(buffer, "%ds", seconds); + return buffer; +} + DynamicConfig PrintStatistics::config() const { DynamicConfig config; - std::string normal_print_time = this->estimated_normal_print_time; - std::string silent_print_time = this->estimated_silent_print_time; - normal_print_time.erase(std::remove_if(normal_print_time.begin(), normal_print_time.end(), std::isspace), normal_print_time.end()); - silent_print_time.erase(std::remove_if(silent_print_time.begin(), silent_print_time.end(), std::isspace), silent_print_time.end()); + std::string normal_print_time = short_time(this->estimated_normal_print_time); + std::string silent_print_time = short_time(this->estimated_silent_print_time); config.set_key_value("print_time", new ConfigOptionString(normal_print_time)); config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time)); config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));