1 #ifndef slic3r_UnsavedChangesDialog_hpp_
2 #define slic3r_UnsavedChangesDialog_hpp_
3 
4 #include <wx/dataview.h>
5 #include <map>
6 #include <vector>
7 
8 #include "GUI_Utils.hpp"
9 #include "wxExtensions.hpp"
10 #include "libslic3r/Preset.hpp"
11 
12 class ScalableButton;
13 class wxStaticText;
14 
15 namespace Slic3r {
16 namespace GUI{
17 
18 // ----------------------------------------------------------------------------
19 //                  ModelNode: a node inside UnsavedChangesModel
20 // ----------------------------------------------------------------------------
21 
22 class ModelNode;
23 using ModelNodePtrArray = std::vector<std::unique_ptr<ModelNode>>;
24 
25 // On all of 3 different platforms Bitmap+Text icon column looks different
26 // because of Markup text is missed or not implemented.
27 // As a temporary workaround, we will use:
28 // MSW - DataViewBitmapText (our custom renderer wxBitmap + wxString, supported Markup text)
29 // OSX - -//-, but Markup text is not implemented right now
30 // GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text)
31 class ModelNode
32 {
33     wxWindow*           m_parent_win{ nullptr };
34 
35     ModelNode*          m_parent;
36     ModelNodePtrArray   m_children;
37     wxBitmap            m_empty_bmp;
38     Preset::Type        m_preset_type {Preset::TYPE_INVALID};
39 
40     std::string         m_icon_name;
41     // saved values for colors if they exist
42     wxString            m_old_color;
43     wxString            m_new_color;
44 
45     // TODO/FIXME:
46     // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded)
47     // needs to know in advance if a node is or _will be_ a container.
48     // Thus implementing:
49     //   bool IsContainer() const
50     //    { return m_children.size()>0; }
51     // doesn't work with wxGTK when UnsavedChangesModel::AddToClassical is called
52     // AND the classical node was removed (a new node temporary without children
53     // would be added to the control)
54     bool                m_container {true};
55 
56 #ifdef __linux__
57     wxIcon              get_bitmap(const wxString& color);
58 #else
59     wxBitmap            get_bitmap(const wxString& color);
60 #endif //__linux__
61 
62 public:
63 
64     bool        m_toggle {true};
65 #ifdef __linux__
66     wxIcon      m_icon;
67     wxIcon      m_old_color_bmp;
68     wxIcon      m_new_color_bmp;
69 #else
70     wxBitmap    m_icon;
71     wxBitmap    m_old_color_bmp;
72     wxBitmap    m_new_color_bmp;
73 #endif //__linux__
74     wxString    m_text;
75     wxString    m_old_value;
76     wxString    m_new_value;
77 
78     // preset(root) node
79     ModelNode(Preset::Type preset_type, wxWindow* parent_win, const wxString& text, const std::string& icon_name);
80 
81     // category node
82     ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name);
83 
84     // group node
85     ModelNode(ModelNode* parent, const wxString& text);
86 
87     // option node
88     ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value);
89 
IsContainer() const90     bool                IsContainer() const         { return m_container; }
IsToggled() const91     bool                IsToggled() const           { return m_toggle; }
Toggle(bool toggle=true)92     void                Toggle(bool toggle = true)  { m_toggle = toggle; }
IsRoot() const93     bool                IsRoot() const              { return m_parent == nullptr; }
type() const94     Preset::Type        type() const                { return m_preset_type; }
text() const95     const wxString&     text() const                { return m_text; }
96 
GetParent()97     ModelNode*          GetParent()                 { return m_parent; }
GetChildren()98     ModelNodePtrArray&  GetChildren()               { return m_children; }
GetNthChild(unsigned int n)99     ModelNode*          GetNthChild(unsigned int n) { return m_children[n].get(); }
GetChildCount() const100     unsigned int        GetChildCount() const       { return m_children.size(); }
101 
Append(std::unique_ptr<ModelNode> child)102     void Append(std::unique_ptr<ModelNode> child)   { m_children.emplace_back(std::move(child)); }
103 
104     void UpdateEnabling();
105     void UpdateIcons();
106 };
107 
108 
109 // ----------------------------------------------------------------------------
110 //                  UnsavedChangesModel
111 // ----------------------------------------------------------------------------
112 
113 class UnsavedChangesModel : public wxDataViewModel
114 {
115     wxWindow*               m_parent_win { nullptr };
116     std::vector<ModelNode*> m_preset_nodes;
117 
118     wxDataViewCtrl*         m_ctrl{ nullptr };
119 
120     ModelNode *AddOption(ModelNode *group_node,
121                          wxString   option_name,
122                          wxString   old_value,
123                          wxString   new_value);
124     ModelNode *AddOptionWithGroup(ModelNode *category_node,
125                                   wxString   group_name,
126                                   wxString   option_name,
127                                   wxString   old_value,
128                                   wxString   new_value);
129     ModelNode *AddOptionWithGroupAndCategory(ModelNode *preset_node,
130                                              wxString   category_name,
131                                              wxString   group_name,
132                                              wxString   option_name,
133                                              wxString   old_value,
134                                              wxString   new_value,
135                                              const std::string category_icon_name);
136 
137 public:
138     enum {
139         colToggle,
140         colIconText,
141         colOldValue,
142         colNewValue,
143         colMax
144     };
145 
146     UnsavedChangesModel(wxWindow* parent);
147     ~UnsavedChangesModel();
148 
SetAssociatedControl(wxDataViewCtrl * ctrl)149     void            SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
150 
151     wxDataViewItem  AddPreset(Preset::Type type, wxString preset_name, PrinterTechnology pt);
152     wxDataViewItem  AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name,
153                               wxString old_value, wxString new_value, const std::string category_icon_name);
154 
155     void            UpdateItemEnabling(wxDataViewItem item);
156     bool            IsEnabledItem(const wxDataViewItem& item);
157 
GetColumnCount() const158     unsigned int    GetColumnCount() const override { return colMax; }
159     wxString        GetColumnType(unsigned int col) const override;
160     void            Rescale();
161 
162     wxDataViewItem  GetParent(const wxDataViewItem& item) const override;
163     unsigned int    GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override;
164 
165     void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override;
166     bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override;
167 
168     bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override;
169     bool IsContainer(const wxDataViewItem& item) const override;
170     // Is the container just a header or an item with all columns
171     // In our case it is an item with all columns
HasContainerColumns(const wxDataViewItem & WXUNUSED (item)) const172     bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
173 };
174 
175 
176 //------------------------------------------
177 //          UnsavedChangesDialog
178 //------------------------------------------
179 class UnsavedChangesDialog : public DPIDialog
180 {
181     wxDataViewCtrl*         m_tree          { nullptr };
182     UnsavedChangesModel*    m_tree_model    { nullptr };
183 
184     ScalableButton*         m_save_btn      { nullptr };
185     ScalableButton*         m_transfer_btn  { nullptr };
186     ScalableButton*         m_discard_btn   { nullptr };
187     wxStaticText*           m_action_line   { nullptr };
188     wxStaticText*           m_info_line     { nullptr };
189     wxCheckBox*             m_remember_choice   { nullptr };
190 
191     bool                    m_empty_selection   { false };
192     bool                    m_has_long_strings  { false };
193     int                     m_save_btn_id       { wxID_ANY };
194     int                     m_move_btn_id       { wxID_ANY };
195     int                     m_continue_btn_id   { wxID_ANY };
196 
197     std::string             m_app_config_key;
198 
199     enum class Action {
200         Undef,
201         Transfer,
202         Discard,
203         Save
204     };
205 
206     static constexpr char ActTransfer[] = "transfer";
207     static constexpr char ActDiscard[]  = "discard";
208     static constexpr char ActSave[]     = "save";
209 
210     // selected action after Dialog closing
211     Action m_exit_action {Action::Undef};
212 
213     struct ItemData
214     {
215         std::string     opt_key;
216         wxString        opt_name;
217         wxString        old_val;
218         wxString        new_val;
219         Preset::Type    type;
220         bool            is_long {false};
221     };
222     // tree items related to the options
223     std::map<wxDataViewItem, ItemData> m_items_map;
224 
225     // preset names which are modified in SavePresetDialog and related types
226     std::vector<std::pair<std::string, Preset::Type>>  names_and_types;
227 
228 public:
229     UnsavedChangesDialog(const wxString& header);
230     UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset);
~UnsavedChangesDialog()231     ~UnsavedChangesDialog() {}
232 
233     wxString get_short_string(wxString full_string);
234 
235     void build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header = "");
236     void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header);
237     void update_tree(Preset::Type type, PresetCollection *presets);
238     void item_value_changed(wxDataViewEvent &event);
239     void context_menu(wxDataViewEvent &event);
240     void show_info_line(Action action, std::string preset_name = "");
241     void update_config(Action action);
242     void close(Action action);
243     bool save(PresetCollection* dependent_presets);
244 
save_preset() const245     bool save_preset() const        { return m_exit_action == Action::Save;     }
transfer_changes() const246     bool transfer_changes() const   { return m_exit_action == Action::Transfer; }
discard() const247     bool discard() const            { return m_exit_action == Action::Discard;  }
248 
249     // get full bundle of preset names and types for saving
get_names_and_types()250     const std::vector<std::pair<std::string, Preset::Type>>& get_names_and_types() { return names_and_types; }
251     // short version of the previous function, for the case, when just one preset is modified
get_preset_name()252     std::string get_preset_name() { return names_and_types[0].first; }
253 
254     std::vector<std::string> get_unselected_options(Preset::Type type);
255     std::vector<std::string> get_selected_options();
256 
257 protected:
258     void on_dpi_changed(const wxRect& suggested_rect) override;
259     void on_sys_color_changed() override;
260 };
261 
262 
263 //------------------------------------------
264 //          FullCompareDialog
265 //------------------------------------------
266 class FullCompareDialog : public wxDialog
267 {
268 public:
269     FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value);
~FullCompareDialog()270     ~FullCompareDialog() {}
271 };
272 
273 }
274 }
275 
276 #endif //slic3r_UnsavedChangesDialog_hpp_
277