mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	UnsavedChangesDialog improvements:
* support markup text and colored icons for cells + Extended BitmapTextRenderer for using of markup text
This commit is contained in:
		
							parent
							
								
									3cf2914a9e
								
							
						
					
					
						commit
						1674d2af29
					
				
					 7 changed files with 664 additions and 102 deletions
				
			
		|  | @ -9,6 +9,12 @@ | |||
| 
 | ||||
| #include <wx/bmpcbox.h> | ||||
| #include <wx/dc.h> | ||||
| #include "wx/generic/private/markuptext.h" | ||||
| #include "wx/generic/private/rowheightcache.h" | ||||
| #include "wx/generic/private/widthcalc.h" | ||||
| #if wxUSE_ACCESSIBILITY | ||||
| #include "wx/private/markupparser.h" | ||||
| #endif // wxUSE_ACCESSIBILITY
 | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
|  | @ -1575,9 +1581,44 @@ wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) | |||
| } | ||||
| #endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
 | ||||
| 
 | ||||
| BitmapTextRenderer::~BitmapTextRenderer() | ||||
| { | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     if (m_markupText) | ||||
|         delete m_markupText; | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
| } | ||||
| 
 | ||||
| #ifdef SUPPORTS_MARKUP | ||||
| void BitmapTextRenderer::EnableMarkup(bool enable) | ||||
| { | ||||
|     if (enable) | ||||
|     { | ||||
|         if (!m_markupText) | ||||
|         { | ||||
|             m_markupText = new wxItemMarkupText(wxString()); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         if (m_markupText) | ||||
|         { | ||||
|             delete m_markupText; | ||||
|             m_markupText = nullptr; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
| 
 | ||||
| bool BitmapTextRenderer::SetValue(const wxVariant &value) | ||||
| { | ||||
|     m_value << value; | ||||
| 
 | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     if (m_markupText) | ||||
|         m_markupText->SetMarkup(m_value.GetText()); | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
|  | @ -1589,6 +1630,11 @@ bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const | |||
| #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY | ||||
| wxString BitmapTextRenderer::GetAccessibleDescription() const | ||||
| { | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     if (m_markupText) | ||||
|         return wxMarkupParser::Strip(m_text); | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
| 
 | ||||
|     return m_value.GetText(); | ||||
| } | ||||
| #endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
 | ||||
|  | @ -1609,7 +1655,17 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) | |||
|         xoffset = icon_sz.x + 4; | ||||
|     } | ||||
| 
 | ||||
|     RenderText(m_value.GetText(), xoffset, rect, dc, state); | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     if (m_markupText) | ||||
|     { | ||||
|         int flags = 0; | ||||
| 
 | ||||
|         rect.x += xoffset; | ||||
|         m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); | ||||
|     } | ||||
|     else | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
|         RenderText(m_value.GetText(), xoffset, rect, dc, state); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
|  | @ -1618,7 +1674,23 @@ wxSize BitmapTextRenderer::GetSize() const | |||
| { | ||||
|     if (!m_value.GetText().empty()) | ||||
|     { | ||||
|         wxSize size = GetTextExtent(m_value.GetText()); | ||||
|         wxSize size; | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|         if (m_markupText) | ||||
|         { | ||||
|             wxDataViewCtrl* const view = GetView(); | ||||
|             wxClientDC dc(view); | ||||
|             if (GetAttr().HasFont()) | ||||
|                 dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); | ||||
| 
 | ||||
|             size = m_markupText->Measure(dc); | ||||
|         } | ||||
|         else | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
|             size = GetTextExtent(m_value.GetText()); | ||||
| 
 | ||||
|         int lines = m_value.GetText().Freq('\n') + 1; | ||||
|         size.SetHeight(size.GetHeight() * lines); | ||||
| 
 | ||||
|         if (m_value.GetBitmap().IsOk()) | ||||
|             size.x += m_value.GetBitmap().GetWidth() + 4; | ||||
|  |  | |||
|  | @ -2,9 +2,10 @@ | |||
| #define slic3r_GUI_ObjectDataViewModel_hpp_ | ||||
| 
 | ||||
| #include <wx/dataview.h> | ||||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "GUI_App.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| enum class ModelVolumeType : int; | ||||
|  | @ -83,9 +84,19 @@ public: | |||
|         ) : | ||||
|     wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), | ||||
|         m_parent(parent) | ||||
|     {} | ||||
|     { | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|         m_markupText = nullptr; | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
|     } | ||||
| #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
 | ||||
| 
 | ||||
|     ~BitmapTextRenderer(); | ||||
| 
 | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     void EnableMarkup(bool enable = true); | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
| 
 | ||||
|     bool SetValue(const wxVariant& value); | ||||
|     bool GetValue(wxVariant& value) const; | ||||
| #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY | ||||
|  | @ -114,6 +125,10 @@ private: | |||
|     DataViewBitmapText  m_value; | ||||
|     bool                m_was_unusable_symbol{ false }; | ||||
|     wxWindow* m_parent{ nullptr }; | ||||
| 
 | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     class wxItemMarkupText* m_markupText; | ||||
| #endif // SUPPORTS_MARKUP
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -321,6 +321,14 @@ const Option& OptionsSearcher::get_option(size_t pos_in_filter) const | |||
|     return options[found[pos_in_filter].option_idx]; | ||||
| } | ||||
| 
 | ||||
| const Option& OptionsSearcher::get_option(const std::string& opt_key) const | ||||
| { | ||||
|     auto it = std::upper_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); | ||||
|     assert(it != options.end()); | ||||
| 
 | ||||
|     return options[it - options.begin()]; | ||||
| } | ||||
| 
 | ||||
| void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group, const wxString& category) | ||||
| { | ||||
|     groups_and_categories[opt_key] = GroupAndCategory{group, category}; | ||||
|  |  | |||
|  | @ -37,8 +37,8 @@ struct GroupAndCategory { | |||
| }; | ||||
| 
 | ||||
| struct Option { | ||||
|     bool operator<(const Option& other) const { return other.label > this->label; } | ||||
|     bool operator>(const Option& other) const { return other.label < this->label; } | ||||
| //    bool operator<(const Option& other) const { return other.label > this->label; }
 | ||||
|     bool operator<(const Option& other) const { return other.opt_key > this->opt_key; } | ||||
| 
 | ||||
|     // Fuzzy matching works at a character level. Thus matching with wide characters is a safer bet than with short characters,
 | ||||
|     // though for some languages (Chinese?) it may not work correctly.
 | ||||
|  | @ -116,12 +116,18 @@ public: | |||
| 
 | ||||
|     const FoundOption& operator[](const size_t pos) const noexcept { return found[pos]; } | ||||
|     const Option& get_option(size_t pos_in_filter) const; | ||||
|     const Option& get_option(const std::string& opt_key) const; | ||||
| 
 | ||||
|     const std::vector<FoundOption>& found_options() { return found; } | ||||
|     const GroupAndCategory&         get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; } | ||||
|     std::string& search_string() { return search_line; } | ||||
| 
 | ||||
|     void set_printer_technology(PrinterTechnology pt) { printer_technology = pt; } | ||||
| 
 | ||||
|     void sort_options_by_opt_key() { | ||||
|         std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) { | ||||
|             return o1.opt_key < o2.opt_key; }); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -3133,8 +3133,16 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, | |||
| bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) | ||||
| { | ||||
|     UnsavedChangesDialog dlg(m_type); | ||||
|     dlg.ShowModal(); | ||||
| 
 | ||||
|     if (dlg.ShowModal() == wxID_CANCEL) | ||||
|         return false; | ||||
|     if (dlg.just_continue()) | ||||
|         return true; | ||||
|     if (dlg.save_preset()) | ||||
|         // save selected changes
 | ||||
|         return false; | ||||
|     if (dlg.move_preset()) | ||||
|         // move selected changes
 | ||||
|         return false; | ||||
| 
 | ||||
|     if (presets == nullptr) presets = m_presets; | ||||
|     // Display a dialog showing the dirty options in a human readable form.
 | ||||
|  |  | |||
|  | @ -13,11 +13,12 @@ | |||
| #include "GUI_App.hpp" | ||||
| #include "Plater.hpp" | ||||
| #include "Tab.hpp" | ||||
| #include "ObjectDataViewModel.hpp" | ||||
| 
 | ||||
| #define FTS_FUZZY_MATCH_IMPLEMENTATION | ||||
| #include "fts_fuzzy_match.h" | ||||
| 
 | ||||
| #include "imgui/imconfig.h" | ||||
| #include "BitmapCache.hpp" | ||||
| 
 | ||||
| using boost::optional; | ||||
| 
 | ||||
|  | @ -29,12 +30,39 @@ namespace GUI { | |||
| //                  ModelNode: a node inside UnsavedChangesModel
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
| static const std::map<Preset::Type, std::string> type_icon_names = { | ||||
|     {Preset::TYPE_PRINT,        "cog"           }, | ||||
|     {Preset::TYPE_SLA_PRINT,    "cog"           }, | ||||
|     {Preset::TYPE_FILAMENT,     "spool"         }, | ||||
|     {Preset::TYPE_SLA_MATERIAL, "resin"         }, | ||||
|     {Preset::TYPE_PRINTER,      "sla_printer"   }, | ||||
| }; | ||||
| 
 | ||||
| static std::string black    = "#000000"; | ||||
| static std::string grey     = "#808080"; | ||||
| static std::string orange   = "#ed6b21"; | ||||
| 
 | ||||
| static void color_string(wxString& str, const std::string& color) | ||||
| { | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     str = from_u8((boost::format("<span color=\"%1%\">%2%</span>") % color % into_u8(str)).str()); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void make_string_bold(wxString& str) | ||||
| { | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     str = from_u8((boost::format("<b>%1%</b>") % into_u8(str)).str()); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| // preset(root) node
 | ||||
| ModelNode::ModelNode(const wxString& text, Preset::Type preset_type) : | ||||
| ModelNode::ModelNode(Preset::Type preset_type, const wxString& text) : | ||||
|     m_parent(nullptr), | ||||
|     m_preset_type(preset_type), | ||||
|     m_text(text) | ||||
| { | ||||
|     m_icon = create_scaled_bitmap(type_icon_names.at(preset_type)); | ||||
| } | ||||
| 
 | ||||
| // group node
 | ||||
|  | @ -42,38 +70,230 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& | |||
|     m_parent(parent), | ||||
|     m_text(text) | ||||
| { | ||||
|     m_icon = create_scaled_bitmap(icon_name); | ||||
| } | ||||
| 
 | ||||
| // group node
 | ||||
| ModelNode::ModelNode(ModelNode* parent, const wxString& text, bool is_option) : | ||||
| // category node
 | ||||
| ModelNode::ModelNode(ModelNode* parent, const wxString& text) : | ||||
|     m_parent(parent), | ||||
|     m_text(text), | ||||
|     m_container(!is_option) | ||||
|     m_text(text) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| wxBitmap ModelNode::get_bitmap(const wxString& color) | ||||
| { | ||||
|     /* It's supposed that standard size of an icon is 48px*16px for 100% scaled display.
 | ||||
|      * So set sizes for solid_colored icons used for filament preset | ||||
|      * and scale them in respect to em_unit value | ||||
|      */ | ||||
|     const double em = em_unit(m_parent_win); | ||||
|     const int icon_width    = lround(6.4 * em); | ||||
|     const int icon_height   = lround(1.6 * em); | ||||
| 
 | ||||
|     BitmapCache bmp_cache; | ||||
|     unsigned char rgb[3]; | ||||
|     BitmapCache::parse_color(into_u8(color), rgb); | ||||
|     // there is no need to scale created solid bitmap
 | ||||
|      return bmp_cache.mksolid(icon_width, icon_height, rgb, true); | ||||
| } | ||||
| 
 | ||||
| // option node
 | ||||
| ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value) : | ||||
|     m_parent(parent), | ||||
|     m_old_color(old_value.StartsWith("#") ? old_value : ""), | ||||
|     m_new_color(new_value.StartsWith("#") ? new_value : ""), | ||||
|     m_container(false), | ||||
|     m_text(text), | ||||
|     m_old_value(old_value), | ||||
|     m_new_value(new_value) | ||||
| { | ||||
|     // check if old/new_value is color
 | ||||
|     if (m_old_color.IsEmpty()) { | ||||
|         if (!m_new_color.IsEmpty()) | ||||
|             m_old_value = _L("Undef"); | ||||
|     } | ||||
|     else { | ||||
|         m_old_color_bmp = get_bitmap(m_old_color); | ||||
|         m_old_value.Clear(); | ||||
|     } | ||||
| 
 | ||||
|     if (m_new_color.IsEmpty()) { | ||||
|         if (!m_old_color.IsEmpty()) | ||||
|             m_new_value = _L("Undef"); | ||||
|     } | ||||
|     else { | ||||
|         m_new_color_bmp = get_bitmap(m_new_color); | ||||
|         m_new_value.Clear(); | ||||
|     } | ||||
| 
 | ||||
|     // "color" strings
 | ||||
|     color_string(m_old_value, black); | ||||
|     color_string(m_new_value, orange); | ||||
| } | ||||
| 
 | ||||
| void ModelNode::UpdateEnabling() | ||||
| { | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) | ||||
|     { | ||||
|         std::string old_val = into_u8(str); | ||||
|         boost::replace_all(old_val, clr_from, clr_to); | ||||
|         str = from_u8(old_val); | ||||
|     }; | ||||
| 
 | ||||
|     if (!m_toggle) { | ||||
|         change_text_color(m_text,      black, grey); | ||||
|         change_text_color(m_old_value, black, grey); | ||||
|         change_text_color(m_new_value, orange,grey); | ||||
|     } | ||||
|     else { | ||||
|         change_text_color(m_text,      grey, black); | ||||
|         change_text_color(m_old_value, grey, black); | ||||
|         change_text_color(m_new_value, grey, orange); | ||||
|     } | ||||
| #endif | ||||
|     // update icons for the colors
 | ||||
|     if (!m_old_color.IsEmpty()) | ||||
|          m_old_color_bmp = get_bitmap(m_toggle? m_old_color : grey); | ||||
|     if (!m_new_color.IsEmpty()) | ||||
|          m_new_color_bmp = get_bitmap(m_toggle? m_new_color : grey); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| //                          UnsavedChangesModel
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
| UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) | ||||
| UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) : | ||||
|     m_parent_win(parent) | ||||
| { | ||||
|     int icon_id = 0; | ||||
|     for (const std::string& icon : { "cog", "printer", "sla_printer", "spool", "resin" }) | ||||
|         m_icon[icon_id++] = ScalableBitmap(parent, icon); | ||||
| 
 | ||||
|     m_root = new ModelNode("Preset", Preset::TYPE_INVALID); | ||||
| } | ||||
| 
 | ||||
| UnsavedChangesModel::~UnsavedChangesModel() | ||||
| { | ||||
|     delete m_root; | ||||
|     for (ModelNode* preset_node : m_preset_nodes) | ||||
|         delete preset_node; | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset_name) | ||||
| { | ||||
|     // "color" strings
 | ||||
|     color_string(preset_name, black); | ||||
|     make_string_bold(preset_name); | ||||
| 
 | ||||
|     auto preset = new ModelNode(type, preset_name); | ||||
|     m_preset_nodes.emplace_back(preset); | ||||
| 
 | ||||
|     wxDataViewItem child((void*)preset); | ||||
|     wxDataViewItem parent(nullptr); | ||||
| 
 | ||||
|     ItemAdded(parent, child); | ||||
|     return child; | ||||
| } | ||||
| 
 | ||||
| ModelNode* UnsavedChangesModel::AddOption(ModelNode* group_node, wxString option_name, wxString old_value, wxString new_value) | ||||
| { | ||||
|     ModelNode* option = new ModelNode(group_node, option_name, old_value, new_value); | ||||
|     group_node->Append(option); | ||||
|     ItemAdded(wxDataViewItem((void*)group_node), wxDataViewItem((void*)option)); | ||||
| 
 | ||||
|     return option; | ||||
| } | ||||
| 
 | ||||
| ModelNode* UnsavedChangesModel::AddOptionWithGroup(ModelNode* category_node, wxString group_name, wxString option_name, wxString old_value, wxString new_value) | ||||
| { | ||||
|     ModelNode* group_node = new ModelNode(category_node, group_name); | ||||
|     category_node->Append(group_node); | ||||
|     wxDataViewItem group_item = wxDataViewItem((void*)group_node); | ||||
|     ItemAdded(wxDataViewItem((void*)category_node), group_item); | ||||
|     m_ctrl->Expand(group_item); | ||||
| 
 | ||||
|     return AddOption(group_node, option_name, old_value, new_value); | ||||
| } | ||||
| 
 | ||||
| ModelNode* UnsavedChangesModel::AddOptionWithGroupAndCategory(ModelNode* preset_node, wxString category_name, wxString group_name, wxString option_name, wxString old_value, wxString new_value) | ||||
| { | ||||
|     ModelNode* category_node = new ModelNode(preset_node, category_name, "cog"); | ||||
|     preset_node->Append(category_node); | ||||
|     ItemAdded(wxDataViewItem((void*)preset_node), wxDataViewItem((void*)category_node)); | ||||
| 
 | ||||
|     return AddOptionWithGroup(category_node, group_name, option_name, old_value, new_value); | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem UnsavedChangesModel::AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name, | ||||
|                                               wxString old_value, wxString new_value) | ||||
| { | ||||
|     // "color" strings
 | ||||
|     color_string(category_name, black); | ||||
|     color_string(group_name,    black); | ||||
|     color_string(option_name,   black); | ||||
| 
 | ||||
|     // "make" strings bold
 | ||||
|     make_string_bold(category_name); | ||||
|     make_string_bold(group_name); | ||||
| 
 | ||||
|     // add items
 | ||||
|     for (ModelNode* preset : m_preset_nodes) | ||||
|         if (preset->type() == type) | ||||
|         { | ||||
|             for (ModelNode* category : preset->GetChildren()) | ||||
|                 if (category->text() == category_name) | ||||
|                 { | ||||
|                     for (ModelNode* group : category->GetChildren()) | ||||
|                         if (group->text() == group_name) | ||||
|                             return wxDataViewItem((void*)AddOption(group, option_name, old_value, new_value)); | ||||
|                      | ||||
|                     return wxDataViewItem((void*)AddOptionWithGroup(category, group_name, option_name, old_value, new_value)); | ||||
|                 } | ||||
| 
 | ||||
|             return wxDataViewItem((void*)AddOptionWithGroupAndCategory(preset, category_name, group_name, option_name, old_value, new_value)); | ||||
|         } | ||||
| 
 | ||||
|     return wxDataViewItem(nullptr);     | ||||
| } | ||||
| 
 | ||||
| static void update_children(ModelNode* parent) | ||||
| { | ||||
|     if (parent->IsContainer()) { | ||||
|         bool toggle = parent->IsToggled(); | ||||
|         for (ModelNode* child : parent->GetChildren()) { | ||||
|             child->Toggle(toggle); | ||||
|             child->UpdateEnabling(); | ||||
|             update_children(child); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void update_parents(ModelNode* node) | ||||
| { | ||||
|     ModelNode* parent = node->GetParent(); | ||||
|     if (parent) { | ||||
|         bool toggle = false; | ||||
|         for (ModelNode* child : parent->GetChildren()) { | ||||
|             if (child->IsToggled()) { | ||||
|                 toggle = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         parent->Toggle(toggle); | ||||
|         parent->UpdateEnabling(); | ||||
|         update_parents(parent); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void UnsavedChangesModel::UpdateItemEnabling(wxDataViewItem item) | ||||
| { | ||||
|     assert(item.IsOk()); | ||||
|     ModelNode* node = (ModelNode*)item.GetID(); | ||||
|     node->UpdateEnabling(); | ||||
| 
 | ||||
|     update_children(node); | ||||
|     update_parents(node);     | ||||
| } | ||||
| 
 | ||||
| void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const | ||||
| { | ||||
|     wxASSERT(item.IsOk()); | ||||
|     assert(item.IsOk()); | ||||
| 
 | ||||
|     ModelNode* node = (ModelNode*)item.GetID(); | ||||
|     switch (col) | ||||
|  | @ -81,20 +301,14 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite | |||
|     case colToggle: | ||||
|         variant = node->m_toggle; | ||||
|         break; | ||||
|     case colTypeIcon: | ||||
|         variant << node->m_type_icon; | ||||
|         break; | ||||
|     case colGroupIcon: | ||||
|         variant << node->m_group_icon; | ||||
|         break; | ||||
|     case colMarkedText: | ||||
|         variant =node->m_text; | ||||
|     case colIconText: | ||||
|         variant << DataViewBitmapText(node->m_text, node->m_icon); | ||||
|         break; | ||||
|     case colOldValue: | ||||
|         variant =node->m_text; | ||||
|         variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp); | ||||
|         break; | ||||
|     case colNewValue: | ||||
|         variant =node->m_text; | ||||
|         variant << DataViewBitmapText(node->m_new_value, node->m_new_color_bmp); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|  | @ -109,24 +323,27 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte | |||
|     ModelNode* node = (ModelNode*)item.GetID(); | ||||
|     switch (col) | ||||
|     { | ||||
|     case colIconText: { | ||||
|         DataViewBitmapText data; | ||||
|         data << variant; | ||||
|         node->m_icon = data.GetBitmap(); | ||||
|         node->m_text = data.GetText(); | ||||
|         return true; } | ||||
|     case colToggle: | ||||
|         node->m_toggle = variant.GetBool(); | ||||
|         return true; | ||||
|     case colTypeIcon: | ||||
|         node->m_type_icon << variant; | ||||
|         return true; | ||||
|     case colGroupIcon: | ||||
|         node->m_group_icon << variant; | ||||
|         return true; | ||||
|     case colMarkedText: | ||||
|         node->m_text = variant.GetString(); | ||||
|         return true; | ||||
|     case colOldValue: | ||||
|         node->m_text = variant.GetString(); | ||||
|         return true; | ||||
|     case colNewValue: | ||||
|         node->m_text = variant.GetString(); | ||||
|         return true; | ||||
|     case colOldValue: { | ||||
|         DataViewBitmapText data; | ||||
|         data << variant; | ||||
|         node->m_old_color_bmp   = data.GetBitmap(); | ||||
|         node->m_old_value       = data.GetText(); | ||||
|         return true; } | ||||
|     case colNewValue: { | ||||
|         DataViewBitmapText data; | ||||
|         data << variant; | ||||
|         node->m_new_color_bmp   = data.GetBitmap(); | ||||
|         node->m_new_value       = data.GetText(); | ||||
|         return true; } | ||||
|     default: | ||||
|         wxLogError("UnsavedChangesModel::SetValue: wrong column"); | ||||
|     } | ||||
|  | @ -136,11 +353,11 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte | |||
| bool UnsavedChangesModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const | ||||
| { | ||||
|     assert(item.IsOk()); | ||||
| 
 | ||||
|     ModelNode* node = (ModelNode*)item.GetID(); | ||||
|     if (col == colToggle) | ||||
|         return true; | ||||
| 
 | ||||
|     // disable unchecked nodes
 | ||||
|     return !node->IsToggle(); | ||||
|     return ((ModelNode*)item.GetID())->IsToggled(); | ||||
| } | ||||
| 
 | ||||
| wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const | ||||
|  | @ -152,7 +369,7 @@ wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const | |||
|     ModelNode* node = (ModelNode*)item.GetID(); | ||||
| 
 | ||||
|     // "MyMusic" also has no parent
 | ||||
|     if (node == m_root) | ||||
|     if (node->IsRoot()) | ||||
|         return wxDataViewItem(nullptr); | ||||
| 
 | ||||
|     return wxDataViewItem((void*)node->GetParent()); | ||||
|  | @ -172,8 +389,9 @@ unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDa | |||
| { | ||||
|     ModelNode* node = (ModelNode*)parent.GetID(); | ||||
|     if (!node) { | ||||
|         array.Add(wxDataViewItem((void*)m_root)); | ||||
|         return 1; | ||||
|         for (auto preset_node : m_preset_nodes) | ||||
|             array.Add(wxDataViewItem((void*)preset_node)); | ||||
|         return m_preset_nodes.size(); | ||||
|     } | ||||
| 
 | ||||
|     if (node->GetChildCount() == 0) | ||||
|  | @ -191,13 +409,16 @@ unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDa | |||
| 
 | ||||
| wxString UnsavedChangesModel::GetColumnType(unsigned int col) const | ||||
| { | ||||
|     if (col == colToggle) | ||||
|     switch (col) | ||||
|     { | ||||
|     case colToggle: | ||||
|         return "bool"; | ||||
|      | ||||
|     if (col < colMarkedText) | ||||
|         return "wxBitmap"; | ||||
|      | ||||
|     return "string"; | ||||
|     case colIconText: | ||||
|     case colOldValue: | ||||
|     case colNewValue: | ||||
|     default: | ||||
|         return "DataViewBitmapText";//"string";
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -214,43 +435,215 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) | |||
|     int border = 10; | ||||
|     int em = em_unit(); | ||||
| 
 | ||||
|     changes_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 60), wxBORDER_SIMPLE); | ||||
|     changes_tree_model = new UnsavedChangesModel(this); | ||||
|     changes_tree->AssociateModel(changes_tree_model); | ||||
|     m_tree       = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT); | ||||
|     m_tree_model = new UnsavedChangesModel(this); | ||||
|     m_tree->AssociateModel(m_tree_model); | ||||
|     m_tree_model->SetAssociatedControl(m_tree); | ||||
| 
 | ||||
|     changes_tree->AppendToggleColumn(L"\u2610", UnsavedChangesModel::colToggle);//2610,11,12 //2714
 | ||||
|     changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colTypeIcon); | ||||
|     changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colGroupIcon); | ||||
| 
 | ||||
|     wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer(); | ||||
|     m_tree->AppendToggleColumn(/*L"\u2714"*/"", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em, wxALIGN_NOT);//2610,11,12 //2714
 | ||||
| 
 | ||||
|     BitmapTextRenderer* renderer = new BitmapTextRenderer(m_tree); | ||||
| #ifdef SUPPORTS_MARKUP | ||||
|     markupRenderer->EnableMarkup(); | ||||
|     renderer->EnableMarkup(); | ||||
| #endif | ||||
|     m_tree->AppendColumn(new wxDataViewColumn("",           renderer, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); | ||||
|     m_tree->AppendColumn(new wxDataViewColumn("Old value",  renderer, UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); | ||||
|     m_tree->AppendColumn(new wxDataViewColumn("New value",  renderer, UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); | ||||
| 
 | ||||
|     changes_tree->AppendColumn(new wxDataViewColumn("", markupRenderer, UnsavedChangesModel::colMarkedText, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); | ||||
|     changes_tree->AppendColumn(new wxDataViewColumn("Old value", markupRenderer, UnsavedChangesModel::colOldValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); | ||||
|     changes_tree->AppendColumn(new wxDataViewColumn("New value", markupRenderer, UnsavedChangesModel::colNewValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); | ||||
|     m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); | ||||
| 
 | ||||
|     wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL); | ||||
|     wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); | ||||
| 
 | ||||
|     Tab* tab = wxGetApp().get_tab(type); | ||||
|     assert(tab); | ||||
| 
 | ||||
|     PresetCollection* presets = tab->get_presets(); | ||||
| 
 | ||||
|     wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); | ||||
|     auto save_btn = new wxButton(this, m_save_btn_id = NewControlId(), label); | ||||
|     save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); | ||||
|     buttons->Insert(0, save_btn, 0, wxLEFT, 5); | ||||
| 
 | ||||
|     label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"NewSelectedPreset\"")).str()); | ||||
|     auto move_btn = new wxButton(this, m_move_btn_id = NewControlId(), label); | ||||
|     move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); | ||||
|     buttons->Insert(1, move_btn, 0, wxLEFT, 5); | ||||
| 
 | ||||
|     auto continue_btn = new wxButton(this, m_continue_btn_id = NewControlId(), _L("Continue without changes")); | ||||
|     continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); | ||||
|     buttons->Insert(2, continue_btn, 0, wxLEFT, 5); | ||||
| 
 | ||||
|     wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); | ||||
| 
 | ||||
|     topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for the current preset") + ":"), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); | ||||
|     topSizer->Add(changes_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); | ||||
|     topSizer->Add(cancel_btn, 0, wxEXPAND | wxALL, border); | ||||
|     topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); | ||||
|     topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); | ||||
|     topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); | ||||
| 
 | ||||
|     update(type); | ||||
| 
 | ||||
|     SetSizer(topSizer); | ||||
|     topSizer->SetSizeHints(this); | ||||
| } | ||||
| 
 | ||||
| void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) | ||||
| { | ||||
|     if (event.GetColumn() != UnsavedChangesModel::colToggle) | ||||
|         return; | ||||
| 
 | ||||
|     wxDataViewItem item = event.GetItem(); | ||||
| 
 | ||||
|     m_tree_model->UpdateItemEnabling(item); | ||||
|     m_tree->Refresh(); | ||||
| } | ||||
| 
 | ||||
| void UnsavedChangesDialog::close(Action action) | ||||
| { | ||||
|     m_action = action; | ||||
|     this->EndModal(wxID_CLOSE); | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config) | ||||
| { | ||||
|     const std::vector<std::string>& names = config.def()->options.at(opt_key).enum_labels;//ConfigOptionEnum<T>::get_enum_names();
 | ||||
|     T val = config.option<ConfigOptionEnum<T>>(opt_key)->value; | ||||
|     return from_u8(_u8L(names[static_cast<int>(val)])); | ||||
| } | ||||
| 
 | ||||
| static wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config) | ||||
| { | ||||
|     wxString out; | ||||
| 
 | ||||
|     // FIXME controll, if opt_key has index
 | ||||
|     int opt_idx = 0; | ||||
| 
 | ||||
|     ConfigOptionType type = config.def()->options.at(opt_key).type; | ||||
| 
 | ||||
|     switch (type) { | ||||
|     case coInt: | ||||
|         return from_u8((boost::format("%1%") % config.opt_int(opt_key)).str()); | ||||
|     case coInts: { | ||||
|         const ConfigOptionInts* opt = config.opt<ConfigOptionInts>(opt_key); | ||||
|         if (opt) | ||||
|             return from_u8((boost::format("%1%") % opt->get_at(opt_idx)).str()); | ||||
|         break; | ||||
|     } | ||||
|     case coBool: | ||||
|         return config.opt_bool(opt_key) ? "true" : "false"; | ||||
|     case coBools: { | ||||
|         const ConfigOptionBools* opt = config.opt<ConfigOptionBools>(opt_key); | ||||
|         if (opt) | ||||
|             return opt->get_at(opt_idx) ? "true" : "false"; | ||||
|         break; | ||||
|     } | ||||
|     case coPercent: | ||||
|         return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str()); | ||||
|     case coPercents: { | ||||
|         const ConfigOptionPercents* opt = config.opt<ConfigOptionPercents>(opt_key); | ||||
|         if (opt) | ||||
|             return from_u8((boost::format("%1%%%") % int(opt->get_at(opt_idx))).str()); | ||||
|         break; | ||||
|     } | ||||
|     case coFloat: | ||||
|         return double_to_string(config.opt_float(opt_key)); | ||||
|     case coFloats: { | ||||
|         const ConfigOptionFloats* opt = config.opt<ConfigOptionFloats>(opt_key); | ||||
|         if (opt) | ||||
|             return double_to_string(opt->get_at(opt_idx)); | ||||
|         break; | ||||
|     } | ||||
|     case coString: | ||||
|         return from_u8(config.opt_string(opt_key)); | ||||
|     case coStrings: { | ||||
|         const ConfigOptionStrings* strings = config.opt<ConfigOptionStrings>(opt_key); | ||||
|         if (strings) { | ||||
|             if (opt_key == "compatible_printers" || opt_key == "compatible_prints") { | ||||
|                 if (strings->empty()) | ||||
|                     return _L("All");  | ||||
|                 for (size_t id = 0; id < strings->size(); id++) | ||||
|                     out += from_u8(strings->get_at(id)) + "\n"; | ||||
|                 out.RemoveLast(1); | ||||
|                 return out; | ||||
|             } | ||||
|             if (!strings->empty()) | ||||
|                 return from_u8(strings->get_at(opt_idx)); | ||||
|         } | ||||
|         break; | ||||
|         } | ||||
|     case coFloatOrPercent: { | ||||
|         const ConfigOptionFloatOrPercent* opt = config.opt<ConfigOptionFloatOrPercent>(opt_key); | ||||
|         if (opt) | ||||
|             out = double_to_string(opt->value) + (opt->percent ? "%" : ""); | ||||
|         return out; | ||||
|     } | ||||
|     case coEnum: { | ||||
|         if (opt_key == "top_fill_pattern" || | ||||
|             opt_key == "bottom_fill_pattern" || | ||||
|             opt_key == "fill_pattern") | ||||
|             return get_string_from_enum<InfillPattern>(opt_key, config); | ||||
|         if (opt_key == "gcode_flavor") | ||||
|             return get_string_from_enum<GCodeFlavor>(opt_key, config); | ||||
|         if (opt_key == "ironing_type") | ||||
|             return get_string_from_enum<IroningType>(opt_key, config); | ||||
|         if (opt_key == "support_material_pattern") | ||||
|             return get_string_from_enum<SupportMaterialPattern>(opt_key, config); | ||||
|         if (opt_key == "seam_position") | ||||
|             return get_string_from_enum<SeamPosition>(opt_key, config); | ||||
|         if (opt_key == "display_orientation") | ||||
|             return get_string_from_enum<SLADisplayOrientation>(opt_key, config); | ||||
|         if (opt_key == "support_pillar_connection_mode") | ||||
|             return get_string_from_enum<SLAPillarConnectionMode>(opt_key, config); | ||||
|         break; | ||||
|     } | ||||
|     case coPoints: { | ||||
|             /*
 | ||||
|         if (opt_key == "bed_shape") { | ||||
|             config.option<ConfigOptionPoints>(opt_key)->values = boost::any_cast<std::vector<Vec2d>>(value); | ||||
|             break; | ||||
|         } | ||||
|         ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast<Vec2d>(value) }; | ||||
|         config.option<ConfigOptionPoints>(opt_key)->set_at(vec_new, opt_index, 0); | ||||
|     */ | ||||
|         return "Points"; | ||||
|     } | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| void UnsavedChangesDialog::update(Preset::Type type) | ||||
| { | ||||
|     Tab* tab = wxGetApp().get_tab(type); | ||||
|     assert(tab); | ||||
| 
 | ||||
|     PresetCollection* presets = tab->get_presets(); | ||||
|     // Display a dialog showing the dirty options in a human readable form.
 | ||||
|     const DynamicPrintConfig& old_config = presets->get_selected_preset().config; | ||||
|     const DynamicPrintConfig& new_config = presets->get_edited_preset().config; | ||||
| 
 | ||||
|     m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); | ||||
| 
 | ||||
|     Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); | ||||
|     searcher.sort_options_by_opt_key(); | ||||
| 
 | ||||
|     // Collect dirty options.
 | ||||
|     for (const std::string& opt_key : presets->current_dirty_options()) { | ||||
|         const Search::Option& option = searcher.get_option(opt_key); | ||||
| 
 | ||||
|         m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local,  | ||||
|                                 get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) | ||||
| { | ||||
|     const int& em = em_unit(); | ||||
| 
 | ||||
|     msw_buttons_rescale(this, em, { wxID_CANCEL }); | ||||
|     msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id }); | ||||
| 
 | ||||
|     const wxSize& size = wxSize(80 * em, 60 * em); | ||||
|     const wxSize& size = wxSize(80 * em, 30 * em); | ||||
|     SetMinSize(size); | ||||
| 
 | ||||
|     Fit(); | ||||
|  | @ -260,7 +653,7 @@ void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) | |||
| void UnsavedChangesDialog::on_sys_color_changed() | ||||
| { | ||||
|     // msw_rescale updates just icons, so use it
 | ||||
| //    changes_tree_model->msw_rescale();
 | ||||
| //    m_tree_model->msw_rescale();
 | ||||
| 
 | ||||
|     Refresh(); | ||||
| } | ||||
|  |  | |||
|  | @ -31,11 +31,17 @@ WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); | |||
| 
 | ||||
| class ModelNode | ||||
| { | ||||
|     wxWindow* m_parent_win{ nullptr }; | ||||
| 
 | ||||
|     ModelNode*          m_parent; | ||||
|     ModelNodePtrArray   m_children; | ||||
|     wxBitmap            m_empty_bmp; | ||||
|     Preset::Type        m_preset_type {Preset::TYPE_INVALID}; | ||||
| 
 | ||||
|     // saved values for colors if they exist
 | ||||
|     wxString            m_old_color; | ||||
|     wxString            m_new_color; | ||||
| 
 | ||||
|     // TODO/FIXME:
 | ||||
|     // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded)
 | ||||
|     // needs to know in advance if a node is or _will be_ a container.
 | ||||
|  | @ -47,23 +53,29 @@ class ModelNode | |||
|     // would be added to the control)
 | ||||
|     bool                m_container {true}; | ||||
| 
 | ||||
|     wxBitmap            get_bitmap(const wxString& color); | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     bool        m_toggle {true}; | ||||
|     wxBitmap    m_type_icon; | ||||
|     wxBitmap    m_group_icon; | ||||
|     wxBitmap    m_icon; | ||||
|     wxBitmap    m_old_color_bmp; | ||||
|     wxBitmap    m_new_color_bmp; | ||||
|     wxString    m_text; | ||||
|     wxString    m_old_value; | ||||
|     wxString    m_new_value; | ||||
| 
 | ||||
|     // preset(root) node
 | ||||
|     ModelNode(const wxString& text, Preset::Type preset_type); | ||||
|     ModelNode(Preset::Type preset_type, const wxString& text); | ||||
| 
 | ||||
|     // group node
 | ||||
|     // category node
 | ||||
|     ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name); | ||||
| 
 | ||||
|     // group node
 | ||||
|     ModelNode(ModelNode* parent, const wxString& text, bool is_option); | ||||
|     ModelNode(ModelNode* parent, const wxString& text); | ||||
| 
 | ||||
|     // option node
 | ||||
|     ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value); | ||||
| 
 | ||||
|     ~ModelNode() { | ||||
|         // free all our children nodes
 | ||||
|  | @ -75,15 +87,21 @@ public: | |||
|     } | ||||
| 
 | ||||
|     bool                IsContainer() const         { return m_container; } | ||||
|     bool                IsToggle() const            { return m_toggle; } | ||||
|     bool                IsToggled() const           { return m_toggle; } | ||||
|     void                Toggle(bool toggle = true)  { m_toggle = toggle; } | ||||
|     bool                IsRoot() const              { return m_parent == nullptr; } | ||||
|     Preset::Type        type() const                { return m_preset_type; } | ||||
|     const wxString&     text() const                { return m_text; } | ||||
| 
 | ||||
|     ModelNode*          GetParent()                 { return m_parent; } | ||||
|     ModelNodePtrArray&  GetChildren()               { return m_children; } | ||||
|     ModelNode*          GetNthChild(unsigned int n) { return m_children.Item(n); } | ||||
|     unsigned int        GetChildCount() const       { return m_children.GetCount(); } | ||||
| 
 | ||||
|     void Insert(ModelNode* child, unsigned int n) { m_children.Insert(child, n); } | ||||
|     void Append(ModelNode* child) { m_children.Add(child); } | ||||
|     void Insert(ModelNode* child, unsigned int n)   { m_children.Insert(child, n); } | ||||
|     void Append(ModelNode* child)                   { m_children.Add(child); } | ||||
| 
 | ||||
|     void UpdateEnabling(); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -93,15 +111,31 @@ public: | |||
| 
 | ||||
| class UnsavedChangesModel : public wxDataViewModel | ||||
| { | ||||
|     ModelNode* m_root; | ||||
|     ScalableBitmap                          m_icon[5]; | ||||
|     wxWindow*               m_parent_win {nullptr}; | ||||
|     std::vector<ModelNode*> m_preset_nodes; | ||||
| 
 | ||||
|     wxDataViewCtrl*         m_ctrl{ nullptr }; | ||||
| 
 | ||||
|     ModelNode *AddOption(ModelNode *group_node, | ||||
|                          wxString   option_name, | ||||
|                          wxString   old_value, | ||||
|                          wxString   new_value); | ||||
|     ModelNode *AddOptionWithGroup(ModelNode *category_node, | ||||
|                                   wxString   group_name, | ||||
|                                   wxString   option_name, | ||||
|                                   wxString   old_value, | ||||
|                                   wxString   new_value); | ||||
|     ModelNode *AddOptionWithGroupAndCategory(ModelNode *preset_node, | ||||
|                                              wxString   category_name, | ||||
|                                              wxString   group_name, | ||||
|                                              wxString   option_name, | ||||
|                                              wxString   old_value, | ||||
|                                              wxString   new_value); | ||||
| 
 | ||||
| public: | ||||
|     enum { | ||||
|         colToggle, | ||||
|         colTypeIcon, | ||||
|         colGroupIcon, | ||||
|         colMarkedText, | ||||
|         colIconText, | ||||
|         colOldValue, | ||||
|         colNewValue, | ||||
|         colMax | ||||
|  | @ -110,18 +144,28 @@ public: | |||
|     UnsavedChangesModel(wxWindow* parent); | ||||
|     ~UnsavedChangesModel(); | ||||
| 
 | ||||
|     virtual unsigned int    GetColumnCount() const override { return colMax; } | ||||
|     virtual wxString        GetColumnType(unsigned int col) const override; | ||||
|     void            SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } | ||||
| 
 | ||||
|     virtual wxDataViewItem  GetParent(const wxDataViewItem& item) const override; | ||||
|     virtual unsigned int    GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; | ||||
|     wxDataViewItem  AddPreset(Preset::Type type, wxString preset_name); | ||||
|     wxDataViewItem  AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name, | ||||
|                               wxString old_value, wxString new_value); | ||||
| 
 | ||||
|     virtual void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; | ||||
|     virtual bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; | ||||
|     void            UpdateItemEnabling(wxDataViewItem item); | ||||
| 
 | ||||
|     virtual bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; | ||||
|     virtual bool IsContainer(const wxDataViewItem& item) const override; | ||||
|     unsigned int    GetColumnCount() const override { return colMax; } | ||||
|     wxString        GetColumnType(unsigned int col) const override; | ||||
| 
 | ||||
|     wxDataViewItem  GetParent(const wxDataViewItem& item) const override; | ||||
|     unsigned int    GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; | ||||
| 
 | ||||
|     void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; | ||||
|     bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; | ||||
| 
 | ||||
|     bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; | ||||
|     bool IsContainer(const wxDataViewItem& item) const override; | ||||
|     // Is the container just a header or an item with all columns
 | ||||
|     // In our case it is an item with all columns
 | ||||
|     bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -130,14 +174,30 @@ public: | |||
| //------------------------------------------
 | ||||
| class UnsavedChangesDialog : public DPIDialog | ||||
| { | ||||
|     wxDataViewCtrl*         changes_tree{ nullptr }; | ||||
|     UnsavedChangesModel*    changes_tree_model{ nullptr }; | ||||
|     wxDataViewCtrl*         m_tree          { nullptr }; | ||||
|     UnsavedChangesModel*    m_tree_model    { nullptr }; | ||||
| 
 | ||||
|     int m_save_btn_id       { wxID_ANY }; | ||||
|     int m_move_btn_id       { wxID_ANY }; | ||||
|     int m_continue_btn_id   { wxID_ANY }; | ||||
| 
 | ||||
|     enum class Action { | ||||
|         Save, | ||||
|         Move, | ||||
|         Continue | ||||
|     } m_action; | ||||
| 
 | ||||
| public: | ||||
|     UnsavedChangesDialog(Preset::Type type); | ||||
|     ~UnsavedChangesDialog() {} | ||||
| 
 | ||||
|     void ProcessSelection(wxDataViewItem selection); | ||||
|     void update(Preset::Type type); | ||||
|     void item_value_changed(wxDataViewEvent &event); | ||||
|     void close(Action action); | ||||
| 
 | ||||
|     bool save_preset() const    { return m_action == Action::Save;      } | ||||
|     bool move_preset() const    { return m_action == Action::Move;      } | ||||
|     bool just_continue() const  { return m_action == Action::Continue;  } | ||||
| 
 | ||||
| protected: | ||||
|     void on_dpi_changed(const wxRect& suggested_rect) override; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 YuSanka
						YuSanka