mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master'
This commit is contained in:
		
						commit
						35a8a5374c
					
				
					 8 changed files with 208 additions and 51 deletions
				
			
		|  | @ -14,6 +14,8 @@ | |||
| 
 | ||||
| #include "PrintExport.hpp" | ||||
| 
 | ||||
| #include <boost/filesystem/path.hpp> | ||||
| 
 | ||||
| //! macro used to mark string used at localization, 
 | ||||
| //! return same string
 | ||||
| #define L(s) Slic3r::I18N::translate(s) | ||||
|  | @ -1864,14 +1866,92 @@ 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); | ||||
| } | ||||
| 
 | ||||
| // 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 = 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)); | ||||
|     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
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -249,6 +249,13 @@ struct PrintStatistics | |||
|     double                          total_wipe_tower_filament; | ||||
|     std::map<size_t, float>         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(); | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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);
 | ||||
|  | @ -337,9 +354,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 +385,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__
 | ||||
|  | @ -455,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(); | ||||
| } | ||||
| 
 | ||||
|  | @ -466,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; | ||||
|  | @ -1667,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<PrusaBitmapTextRenderer*>(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
 | ||||
|  |  | |||
|  | @ -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); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1281,6 +1281,52 @@ 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<PrusaObjectDataViewModel*>(dv_ctrl->GetModel()); | ||||
| 
 | ||||
|     if ( !(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject)) ) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     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()) { | ||||
|         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 || 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; | ||||
| } | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // PrusaDoubleSlider
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
|  |  | |||
|  | @ -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,18 @@ 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; | ||||
|     bool        WasCanceled() const { return m_was_unusable_symbol; } | ||||
| 
 | ||||
| private: | ||||
|     PrusaDataViewBitmapText m_value; | ||||
|     wxBitmap                m_bmp_from_editing_item; | ||||
|     bool                    m_was_unusable_symbol; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros