2020-07-30 14:16:56 +00:00
|
|
|
#include "UnsavedChangesDialog.hpp"
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
#include <string>
|
2020-08-07 13:09:58 +00:00
|
|
|
#include <vector>
|
2020-07-30 14:16:56 +00:00
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <boost/optional.hpp>
|
|
|
|
|
|
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
|
|
#include "libslic3r/PresetBundle.hpp"
|
2020-10-17 12:04:05 +00:00
|
|
|
#include "format.hpp"
|
2020-07-30 14:16:56 +00:00
|
|
|
#include "GUI_App.hpp"
|
|
|
|
#include "Plater.hpp"
|
|
|
|
#include "Tab.hpp"
|
2020-08-10 17:07:45 +00:00
|
|
|
#include "ExtraRenderers.hpp"
|
|
|
|
#include "wxExtensions.hpp"
|
2020-10-03 01:15:54 +00:00
|
|
|
#include "PresetComboBoxes.hpp"
|
2020-10-15 14:48:48 +00:00
|
|
|
#include "MainFrame.hpp"
|
2020-07-30 14:16:56 +00:00
|
|
|
|
2020-08-07 13:09:58 +00:00
|
|
|
//#define FTS_FUZZY_MATCH_IMPLEMENTATION
|
|
|
|
//#include "fts_fuzzy_match.h"
|
2020-07-30 14:16:56 +00:00
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
#include "BitmapCache.hpp"
|
2020-07-30 14:16:56 +00:00
|
|
|
|
|
|
|
using boost::optional;
|
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
|
|
|
namespace GUI {
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// ModelNode: a node inside UnsavedChangesModel
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
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" },
|
2020-08-06 13:54:12 +00:00
|
|
|
{Preset::TYPE_PRINTER, "printer" },
|
2020-08-05 17:25:04 +00:00
|
|
|
};
|
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
static std::string get_icon_name(Preset::Type type, PrinterTechnology pt) {
|
|
|
|
return pt == ptSLA && type == Preset::TYPE_PRINTER ? "sla_printer" : type_icon_names.at(type);
|
|
|
|
}
|
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
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)
|
|
|
|
{
|
2020-08-10 09:24:31 +00:00
|
|
|
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
|
2020-08-05 17:25:04 +00:00
|
|
|
str = from_u8((boost::format("<span color=\"%1%\">%2%</span>") % color % into_u8(str)).str());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void make_string_bold(wxString& str)
|
|
|
|
{
|
2020-08-10 09:24:31 +00:00
|
|
|
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
|
2020-08-05 17:25:04 +00:00
|
|
|
str = from_u8((boost::format("<b>%1%</b>") % into_u8(str)).str());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-07-30 14:16:56 +00:00
|
|
|
// preset(root) node
|
2020-10-07 18:13:12 +00:00
|
|
|
ModelNode::ModelNode(Preset::Type preset_type, wxWindow* parent_win, const wxString& text, const std::string& icon_name) :
|
2020-08-12 11:36:26 +00:00
|
|
|
m_parent_win(parent_win),
|
2020-07-30 14:16:56 +00:00
|
|
|
m_parent(nullptr),
|
|
|
|
m_preset_type(preset_type),
|
2020-10-07 18:13:12 +00:00
|
|
|
m_icon_name(icon_name),
|
2020-07-30 14:16:56 +00:00
|
|
|
m_text(text)
|
|
|
|
{
|
2020-08-12 11:36:26 +00:00
|
|
|
UpdateIcons();
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
// category node
|
2020-07-30 14:16:56 +00:00
|
|
|
ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name) :
|
2020-08-12 11:36:26 +00:00
|
|
|
m_parent_win(parent->m_parent_win),
|
2020-07-30 14:16:56 +00:00
|
|
|
m_parent(parent),
|
2020-08-12 11:36:26 +00:00
|
|
|
m_icon_name(icon_name),
|
2020-07-30 14:16:56 +00:00
|
|
|
m_text(text)
|
|
|
|
{
|
2020-08-12 11:36:26 +00:00
|
|
|
UpdateIcons();
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
// group node
|
2020-08-05 17:25:04 +00:00
|
|
|
ModelNode::ModelNode(ModelNode* parent, const wxString& text) :
|
2020-08-12 11:36:26 +00:00
|
|
|
m_parent_win(parent->m_parent_win),
|
2020-08-05 17:25:04 +00:00
|
|
|
m_parent(parent),
|
2020-10-07 18:13:12 +00:00
|
|
|
m_text(text),
|
|
|
|
m_icon_name("dot_small")
|
2020-08-05 17:25:04 +00:00
|
|
|
{
|
2020-10-07 18:13:12 +00:00
|
|
|
UpdateIcons();
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
|
2020-08-10 09:24:31 +00:00
|
|
|
#ifdef __linux__
|
2020-08-10 09:41:19 +00:00
|
|
|
wxIcon ModelNode::get_bitmap(const wxString& color)
|
2020-08-10 09:24:31 +00:00
|
|
|
#else
|
2020-08-05 17:25:04 +00:00
|
|
|
wxBitmap ModelNode::get_bitmap(const wxString& color)
|
2020-08-10 09:24:31 +00:00
|
|
|
#endif // __linux__
|
2020-08-05 17:25:04 +00:00
|
|
|
{
|
|
|
|
/* 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
|
2020-08-10 17:07:45 +00:00
|
|
|
#ifndef __linux__
|
|
|
|
return bmp_cache.mksolid(icon_width, icon_height, rgb, true);
|
|
|
|
#else
|
2020-08-10 09:24:31 +00:00
|
|
|
wxIcon icon;
|
2020-08-10 17:07:45 +00:00
|
|
|
icon.CopyFromBitmap(bmp_cache.mksolid(icon_width, icon_height, rgb, true));
|
2020-08-10 09:24:31 +00:00
|
|
|
return icon;
|
|
|
|
#endif // __linux__
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// option node
|
|
|
|
ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value) :
|
2020-07-30 14:16:56 +00:00
|
|
|
m_parent(parent),
|
2020-08-05 17:25:04 +00:00
|
|
|
m_old_color(old_value.StartsWith("#") ? old_value : ""),
|
|
|
|
m_new_color(new_value.StartsWith("#") ? new_value : ""),
|
|
|
|
m_container(false),
|
2020-07-30 14:16:56 +00:00
|
|
|
m_text(text),
|
2020-10-07 18:13:12 +00:00
|
|
|
m_icon_name("empty"),
|
2020-08-05 17:25:04 +00:00
|
|
|
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);
|
2020-10-07 18:13:12 +00:00
|
|
|
|
|
|
|
UpdateIcons();
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ModelNode::UpdateEnabling()
|
2020-07-30 14:16:56 +00:00
|
|
|
{
|
2020-08-05 17:25:04 +00:00
|
|
|
auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to)
|
|
|
|
{
|
2020-08-12 11:36:26 +00:00
|
|
|
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
|
2020-08-05 17:25:04 +00:00
|
|
|
std::string old_val = into_u8(str);
|
|
|
|
boost::replace_all(old_val, clr_from, clr_to);
|
|
|
|
str = from_u8(old_val);
|
2020-08-12 11:36:26 +00:00
|
|
|
#endif
|
2020-08-05 17:25:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
// update icons for the colors
|
2020-08-12 11:36:26 +00:00
|
|
|
UpdateIcons();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModelNode::UpdateIcons()
|
|
|
|
{
|
|
|
|
// update icons for the colors, if any exists
|
2020-08-05 17:25:04 +00:00
|
|
|
if (!m_old_color.IsEmpty())
|
2020-08-12 11:36:26 +00:00
|
|
|
m_old_color_bmp = get_bitmap(m_toggle ? m_old_color : grey);
|
2020-08-05 17:25:04 +00:00
|
|
|
if (!m_new_color.IsEmpty())
|
2020-08-12 11:36:26 +00:00
|
|
|
m_new_color_bmp = get_bitmap(m_toggle ? m_new_color : grey);
|
|
|
|
|
|
|
|
// update main icon, if any exists
|
|
|
|
if (m_icon_name.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
m_icon.CopyFromBitmap(create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle));
|
|
|
|
#else
|
|
|
|
m_icon = create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle);
|
|
|
|
#endif //__linux__
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// UnsavedChangesModel
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) :
|
|
|
|
m_parent_win(parent)
|
2020-07-30 14:16:56 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
UnsavedChangesModel::~UnsavedChangesModel()
|
|
|
|
{
|
2020-08-05 17:25:04 +00:00
|
|
|
for (ModelNode* preset_node : m_preset_nodes)
|
|
|
|
delete preset_node;
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset_name, PrinterTechnology pt)
|
2020-08-05 17:25:04 +00:00
|
|
|
{
|
|
|
|
// "color" strings
|
|
|
|
color_string(preset_name, black);
|
|
|
|
make_string_bold(preset_name);
|
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
auto preset = new ModelNode(type, m_parent_win, preset_name, get_icon_name(type, pt));
|
2020-08-05 17:25:04 +00:00
|
|
|
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)
|
|
|
|
{
|
2020-10-16 10:20:51 +00:00
|
|
|
group_node->Append(std::make_unique<ModelNode>(group_node, option_name, old_value, new_value));
|
|
|
|
ModelNode* option = group_node->GetChildren().back().get();
|
2020-08-07 13:09:58 +00:00
|
|
|
wxDataViewItem group_item = wxDataViewItem((void*)group_node);
|
|
|
|
ItemAdded(group_item, wxDataViewItem((void*)option));
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-08-07 13:09:58 +00:00
|
|
|
m_ctrl->Expand(group_item);
|
2020-08-05 17:25:04 +00:00
|
|
|
return option;
|
|
|
|
}
|
|
|
|
|
|
|
|
ModelNode* UnsavedChangesModel::AddOptionWithGroup(ModelNode* category_node, wxString group_name, wxString option_name, wxString old_value, wxString new_value)
|
|
|
|
{
|
2020-10-16 10:20:51 +00:00
|
|
|
category_node->Append(std::make_unique<ModelNode>(category_node, group_name));
|
|
|
|
ModelNode* group_node = category_node->GetChildren().back().get();
|
2020-08-07 13:09:58 +00:00
|
|
|
ItemAdded(wxDataViewItem((void*)category_node), wxDataViewItem((void*)group_node));
|
2020-08-05 17:25:04 +00:00
|
|
|
|
|
|
|
return AddOption(group_node, option_name, old_value, new_value);
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
ModelNode* UnsavedChangesModel::AddOptionWithGroupAndCategory(ModelNode* preset_node, wxString category_name, wxString group_name,
|
|
|
|
wxString option_name, wxString old_value, wxString new_value, const std::string category_icon_name)
|
2020-08-05 17:25:04 +00:00
|
|
|
{
|
2020-10-16 10:20:51 +00:00
|
|
|
preset_node->Append(std::make_unique<ModelNode>(preset_node, category_name, category_icon_name));
|
|
|
|
ModelNode* category_node = preset_node->GetChildren().back().get();
|
2020-08-05 17:25:04 +00:00
|
|
|
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,
|
2020-10-07 18:13:12 +00:00
|
|
|
wxString old_value, wxString new_value, const std::string category_icon_name)
|
2020-08-05 17:25:04 +00:00
|
|
|
{
|
|
|
|
// "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)
|
|
|
|
{
|
2020-10-16 10:20:51 +00:00
|
|
|
for (std::unique_ptr<ModelNode> &category : preset->GetChildren())
|
2020-08-05 17:25:04 +00:00
|
|
|
if (category->text() == category_name)
|
|
|
|
{
|
2020-10-16 10:20:51 +00:00
|
|
|
for (std::unique_ptr<ModelNode> &group : category->GetChildren())
|
2020-08-05 17:25:04 +00:00
|
|
|
if (group->text() == group_name)
|
2020-10-16 10:20:51 +00:00
|
|
|
return wxDataViewItem((void*)AddOption(group.get(), option_name, old_value, new_value));
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-10-16 10:20:51 +00:00
|
|
|
return wxDataViewItem((void*)AddOptionWithGroup(category.get(), group_name, option_name, old_value, new_value));
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
return wxDataViewItem((void*)AddOptionWithGroupAndCategory(preset, category_name, group_name, option_name, old_value, new_value, category_icon_name));
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return wxDataViewItem(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_children(ModelNode* parent)
|
|
|
|
{
|
|
|
|
if (parent->IsContainer()) {
|
|
|
|
bool toggle = parent->IsToggled();
|
2020-10-16 10:20:51 +00:00
|
|
|
for (std::unique_ptr<ModelNode> &child : parent->GetChildren()) {
|
2020-08-05 17:25:04 +00:00
|
|
|
child->Toggle(toggle);
|
|
|
|
child->UpdateEnabling();
|
2020-10-16 10:20:51 +00:00
|
|
|
update_children(child.get());
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_parents(ModelNode* node)
|
|
|
|
{
|
|
|
|
ModelNode* parent = node->GetParent();
|
|
|
|
if (parent) {
|
|
|
|
bool toggle = false;
|
2020-10-16 10:20:51 +00:00
|
|
|
for (std::unique_ptr<ModelNode> &child : parent->GetChildren()) {
|
2020-08-05 17:25:04 +00:00
|
|
|
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);
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
2020-08-10 09:24:31 +00:00
|
|
|
bool UnsavedChangesModel::IsEnabledItem(const wxDataViewItem& item)
|
|
|
|
{
|
|
|
|
assert(item.IsOk());
|
|
|
|
ModelNode* node = (ModelNode*)item.GetID();
|
|
|
|
return node->IsToggled();
|
|
|
|
}
|
|
|
|
|
2020-07-30 14:16:56 +00:00
|
|
|
void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const
|
|
|
|
{
|
2020-08-05 17:25:04 +00:00
|
|
|
assert(item.IsOk());
|
2020-07-30 14:16:56 +00:00
|
|
|
|
|
|
|
ModelNode* node = (ModelNode*)item.GetID();
|
|
|
|
switch (col)
|
|
|
|
{
|
|
|
|
case colToggle:
|
|
|
|
variant = node->m_toggle;
|
|
|
|
break;
|
2020-08-10 07:45:32 +00:00
|
|
|
#ifdef __linux__
|
2020-08-10 09:24:31 +00:00
|
|
|
case colIconText:
|
2020-08-10 07:45:32 +00:00
|
|
|
variant << wxDataViewIconText(node->m_text, node->m_icon);
|
2020-08-10 09:24:31 +00:00
|
|
|
break;
|
|
|
|
case colOldValue:
|
|
|
|
variant << wxDataViewIconText(node->m_old_value, node->m_old_color_bmp);
|
|
|
|
break;
|
|
|
|
case colNewValue:
|
|
|
|
variant << wxDataViewIconText(node->m_new_value, node->m_new_color_bmp);
|
|
|
|
break;
|
2020-08-10 07:45:32 +00:00
|
|
|
#else
|
2020-08-10 09:24:31 +00:00
|
|
|
case colIconText:
|
2020-08-05 17:25:04 +00:00
|
|
|
variant << DataViewBitmapText(node->m_text, node->m_icon);
|
2020-07-30 14:16:56 +00:00
|
|
|
break;
|
|
|
|
case colOldValue:
|
2020-08-05 17:25:04 +00:00
|
|
|
variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp);
|
2020-07-30 14:16:56 +00:00
|
|
|
break;
|
|
|
|
case colNewValue:
|
2020-08-05 17:25:04 +00:00
|
|
|
variant << DataViewBitmapText(node->m_new_value, node->m_new_color_bmp);
|
2020-07-30 14:16:56 +00:00
|
|
|
break;
|
2020-08-10 09:24:31 +00:00
|
|
|
#endif //__linux__
|
2020-07-30 14:16:56 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
wxLogError("UnsavedChangesModel::GetValue: wrong column %d", col);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col)
|
|
|
|
{
|
|
|
|
assert(item.IsOk());
|
|
|
|
|
|
|
|
ModelNode* node = (ModelNode*)item.GetID();
|
|
|
|
switch (col)
|
|
|
|
{
|
2020-08-10 09:24:31 +00:00
|
|
|
case colToggle:
|
|
|
|
node->m_toggle = variant.GetBool();
|
|
|
|
return true;
|
2020-08-10 07:45:32 +00:00
|
|
|
#ifdef __linux__
|
2020-08-10 09:24:31 +00:00
|
|
|
case colIconText: {
|
2020-08-10 07:45:32 +00:00
|
|
|
wxDataViewIconText data;
|
2020-08-05 17:25:04 +00:00
|
|
|
data << variant;
|
2020-08-10 07:45:32 +00:00
|
|
|
node->m_icon = data.GetIcon();
|
2020-08-10 09:24:31 +00:00
|
|
|
node->m_text = data.GetText();
|
|
|
|
return true; }
|
|
|
|
case colOldValue: {
|
|
|
|
wxDataViewIconText data;
|
|
|
|
data << variant;
|
|
|
|
node->m_old_color_bmp = data.GetIcon();
|
|
|
|
node->m_old_value = data.GetText();
|
|
|
|
return true; }
|
|
|
|
case colNewValue: {
|
|
|
|
wxDataViewIconText data;
|
|
|
|
data << variant;
|
|
|
|
node->m_new_color_bmp = data.GetIcon();
|
|
|
|
node->m_new_value = data.GetText();
|
|
|
|
return true; }
|
2020-08-10 07:45:32 +00:00
|
|
|
#else
|
2020-08-10 09:24:31 +00:00
|
|
|
case colIconText: {
|
|
|
|
DataViewBitmapText data;
|
|
|
|
data << variant;
|
2020-08-10 07:45:32 +00:00
|
|
|
node->m_icon = data.GetBitmap();
|
2020-08-10 09:24:31 +00:00
|
|
|
node->m_text = data.GetText();
|
2020-08-05 17:25:04 +00:00
|
|
|
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; }
|
2020-08-10 09:24:31 +00:00
|
|
|
#endif //__linux__
|
2020-07-30 14:16:56 +00:00
|
|
|
default:
|
|
|
|
wxLogError("UnsavedChangesModel::SetValue: wrong column");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UnsavedChangesModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const
|
|
|
|
{
|
|
|
|
assert(item.IsOk());
|
2020-08-05 17:25:04 +00:00
|
|
|
if (col == colToggle)
|
|
|
|
return true;
|
2020-07-30 14:16:56 +00:00
|
|
|
|
|
|
|
// disable unchecked nodes
|
2020-08-05 17:25:04 +00:00
|
|
|
return ((ModelNode*)item.GetID())->IsToggled();
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const
|
|
|
|
{
|
|
|
|
// the invisible root node has no parent
|
|
|
|
if (!item.IsOk())
|
|
|
|
return wxDataViewItem(nullptr);
|
|
|
|
|
|
|
|
ModelNode* node = (ModelNode*)item.GetID();
|
|
|
|
|
|
|
|
// "MyMusic" also has no parent
|
2020-08-05 17:25:04 +00:00
|
|
|
if (node->IsRoot())
|
2020-07-30 14:16:56 +00:00
|
|
|
return wxDataViewItem(nullptr);
|
|
|
|
|
|
|
|
return wxDataViewItem((void*)node->GetParent());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UnsavedChangesModel::IsContainer(const wxDataViewItem& item) const
|
|
|
|
{
|
|
|
|
// the invisble root node can have children
|
|
|
|
if (!item.IsOk())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
ModelNode* node = (ModelNode*)item.GetID();
|
|
|
|
return node->IsContainer();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const
|
|
|
|
{
|
|
|
|
ModelNode* node = (ModelNode*)parent.GetID();
|
|
|
|
if (!node) {
|
2020-08-05 17:25:04 +00:00
|
|
|
for (auto preset_node : m_preset_nodes)
|
|
|
|
array.Add(wxDataViewItem((void*)preset_node));
|
|
|
|
return m_preset_nodes.size();
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
2020-10-16 10:20:51 +00:00
|
|
|
for (std::unique_ptr<ModelNode> &child : node->GetChildren())
|
|
|
|
array.Add(wxDataViewItem((void*)child.get()));
|
2020-07-30 14:16:56 +00:00
|
|
|
|
2020-10-16 10:20:51 +00:00
|
|
|
return node->GetChildCount();
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxString UnsavedChangesModel::GetColumnType(unsigned int col) const
|
|
|
|
{
|
2020-08-05 17:25:04 +00:00
|
|
|
switch (col)
|
|
|
|
{
|
|
|
|
case colToggle:
|
2020-07-30 14:16:56 +00:00
|
|
|
return "bool";
|
2020-08-05 17:25:04 +00:00
|
|
|
case colIconText:
|
|
|
|
case colOldValue:
|
|
|
|
case colNewValue:
|
|
|
|
default:
|
|
|
|
return "DataViewBitmapText";//"string";
|
|
|
|
}
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
2020-08-12 11:36:26 +00:00
|
|
|
static void rescale_children(ModelNode* parent)
|
|
|
|
{
|
|
|
|
if (parent->IsContainer()) {
|
2020-10-16 10:20:51 +00:00
|
|
|
for (std::unique_ptr<ModelNode> &child : parent->GetChildren()) {
|
2020-08-12 11:36:26 +00:00
|
|
|
child->UpdateIcons();
|
2020-10-16 10:20:51 +00:00
|
|
|
rescale_children(child.get());
|
2020-08-12 11:36:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void UnsavedChangesModel::Rescale()
|
|
|
|
{
|
|
|
|
for (ModelNode* node : m_preset_nodes) {
|
|
|
|
node->UpdateIcons();
|
|
|
|
rescale_children(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 14:16:56 +00:00
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
// UnsavedChangesDialog
|
|
|
|
//------------------------------------------
|
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
UnsavedChangesDialog::UnsavedChangesDialog(const wxString& header)
|
2020-10-17 12:04:05 +00:00
|
|
|
: DPIDialog(nullptr, wxID_ANY, _L("Closing PrusaSlicer: Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
2020-08-14 16:17:16 +00:00
|
|
|
{
|
2020-10-15 14:48:48 +00:00
|
|
|
m_app_config_key = "default_action_on_close_application";
|
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
build(Preset::TYPE_INVALID, nullptr, "", header);
|
2020-10-15 14:48:48 +00:00
|
|
|
|
|
|
|
const std::string& def_action = wxGetApp().app_config->get(m_app_config_key);
|
|
|
|
if (def_action == "none")
|
|
|
|
this->CenterOnScreen();
|
|
|
|
else {
|
|
|
|
m_exit_action = def_action == ActSave ? Action::Save : Action::Discard;
|
|
|
|
if (m_exit_action == Action::Save)
|
|
|
|
save(nullptr);
|
|
|
|
}
|
2020-08-14 16:17:16 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 13:45:16 +00:00
|
|
|
UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset)
|
2020-10-17 12:04:05 +00:00
|
|
|
: DPIDialog(nullptr, wxID_ANY, _L("Switching Presets: Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
2020-08-14 16:17:16 +00:00
|
|
|
{
|
2020-10-15 14:48:48 +00:00
|
|
|
m_app_config_key = "default_action_on_select_preset";
|
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
build(type, dependent_presets, new_selected_preset);
|
2020-10-15 14:48:48 +00:00
|
|
|
|
|
|
|
const std::string& def_action = wxGetApp().app_config->get(m_app_config_key);
|
|
|
|
if (def_action == "none") {
|
2020-10-16 11:19:59 +00:00
|
|
|
if (wxGetApp().mainframe->is_dlg_layout() && wxGetApp().mainframe->m_settings_dialog.HasFocus())
|
2020-10-15 14:48:48 +00:00
|
|
|
this->SetPosition(wxGetApp().mainframe->m_settings_dialog.GetPosition());
|
|
|
|
this->CenterOnScreen();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_exit_action = def_action == ActTransfer ? Action::Transfer :
|
|
|
|
def_action == ActSave ? Action::Save : Action::Discard;
|
|
|
|
const PresetCollection& printers = wxGetApp().preset_bundle->printers;
|
|
|
|
if (m_exit_action == Action::Save ||
|
|
|
|
(m_exit_action == Action::Transfer && dependent_presets && (type == dependent_presets->type() ?
|
|
|
|
dependent_presets->get_edited_preset().printer_technology() != dependent_presets->find_preset(new_selected_preset)->printer_technology() :
|
|
|
|
printers.get_edited_preset().printer_technology() != printers.find_preset(new_selected_preset)->printer_technology())) )
|
|
|
|
save(dependent_presets);
|
|
|
|
}
|
2020-08-14 16:17:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header)
|
2020-07-30 14:16:56 +00:00
|
|
|
{
|
|
|
|
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
|
|
|
SetBackgroundColour(bgr_clr);
|
|
|
|
|
2020-09-03 08:44:54 +00:00
|
|
|
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__)
|
2020-08-12 11:36:26 +00:00
|
|
|
// ys_FIXME! temporary workaround for correct font scaling
|
|
|
|
// Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
|
|
|
|
// From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
|
|
|
|
this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
|
|
|
#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
|
|
|
|
|
2020-07-30 14:16:56 +00:00
|
|
|
int border = 10;
|
|
|
|
int em = em_unit();
|
|
|
|
|
2020-08-13 13:45:16 +00:00
|
|
|
m_action_line = new wxStaticText(this, wxID_ANY, "");
|
|
|
|
m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
|
|
|
|
|
2020-10-15 14:48:48 +00:00
|
|
|
m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 60, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES);
|
2020-08-05 17:25:04 +00:00
|
|
|
m_tree_model = new UnsavedChangesModel(this);
|
|
|
|
m_tree->AssociateModel(m_tree_model);
|
|
|
|
m_tree_model->SetAssociatedControl(m_tree);
|
2020-07-30 14:16:56 +00:00
|
|
|
|
2020-08-10 09:24:31 +00:00
|
|
|
m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714
|
2020-08-10 07:45:32 +00:00
|
|
|
|
2020-08-11 07:17:52 +00:00
|
|
|
auto append_bmp_text_column = [this](const wxString& label, unsigned model_column, int width, bool set_expander = false)
|
|
|
|
{
|
2020-08-10 07:45:32 +00:00
|
|
|
#ifdef __linux__
|
2020-08-11 07:17:52 +00:00
|
|
|
wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer();
|
2020-08-10 07:45:32 +00:00
|
|
|
#ifdef SUPPORTS_MARKUP
|
2020-08-11 07:17:52 +00:00
|
|
|
rd->EnableMarkup(true);
|
2020-08-10 07:45:32 +00:00
|
|
|
#endif
|
2020-08-11 07:17:52 +00:00
|
|
|
wxDataViewColumn* column = new wxDataViewColumn(label, rd, model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT);
|
2020-08-10 07:45:32 +00:00
|
|
|
#else
|
2020-10-18 11:44:15 +00:00
|
|
|
wxDataViewColumn* column = new wxDataViewColumn(label, new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE);
|
2020-08-10 07:45:32 +00:00
|
|
|
#endif //__linux__
|
2020-08-11 07:17:52 +00:00
|
|
|
m_tree->AppendColumn(column);
|
|
|
|
if (set_expander)
|
|
|
|
m_tree->SetExpanderColumn(column);
|
|
|
|
};
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-10-15 14:48:48 +00:00
|
|
|
append_bmp_text_column("" , UnsavedChangesModel::colIconText, 28 * em);
|
|
|
|
append_bmp_text_column(_L("Old Value"), UnsavedChangesModel::colOldValue, 12 * em);
|
|
|
|
append_bmp_text_column(_L("New Value"), UnsavedChangesModel::colNewValue, 12 * em);
|
2020-08-07 13:09:58 +00:00
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this);
|
2020-08-10 17:07:45 +00:00
|
|
|
m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this);
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-08-11 07:17:52 +00:00
|
|
|
// Add Buttons
|
2020-10-15 14:48:48 +00:00
|
|
|
wxFont btn_font = this->GetFont().Scaled(1.4f);
|
|
|
|
wxBoxSizer* buttons = new wxBoxSizer(wxHORIZONTAL);
|
2020-08-10 09:24:31 +00:00
|
|
|
|
2020-10-15 14:48:48 +00:00
|
|
|
auto add_btn = [this, buttons, btn_font, dependent_presets](ScalableButton** btn, int& btn_id, const std::string& icon_name, Action close_act, const wxString& label, bool process_enable = true)
|
2020-08-13 13:45:16 +00:00
|
|
|
{
|
2020-10-15 14:48:48 +00:00
|
|
|
*btn = new ScalableButton(this, btn_id = NewControlId(), icon_name, label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24);
|
2020-07-30 14:16:56 +00:00
|
|
|
|
2020-10-15 14:48:48 +00:00
|
|
|
buttons->Add(*btn, 1, wxLEFT, 5);
|
|
|
|
(*btn)->SetFont(btn_font);
|
|
|
|
|
|
|
|
(*btn)->Bind(wxEVT_BUTTON, [this, close_act, dependent_presets](wxEvent&) {
|
|
|
|
update_config(close_act);
|
2020-10-15 17:10:34 +00:00
|
|
|
if (close_act == Action::Save && !save(dependent_presets))
|
|
|
|
return;
|
2020-10-15 14:48:48 +00:00
|
|
|
close(close_act);
|
|
|
|
});
|
2020-08-13 13:45:16 +00:00
|
|
|
if (process_enable)
|
|
|
|
(*btn)->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); });
|
|
|
|
(*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); });
|
|
|
|
};
|
2020-08-10 09:24:31 +00:00
|
|
|
|
2020-09-17 13:25:05 +00:00
|
|
|
const PresetCollection& printers = wxGetApp().preset_bundle->printers;
|
2020-10-15 14:48:48 +00:00
|
|
|
if (dependent_presets && (type == dependent_presets->type() ?
|
|
|
|
dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() :
|
|
|
|
printers.get_edited_preset().printer_technology() == printers.find_preset(new_selected_preset)->printer_technology()))
|
|
|
|
add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Transfer"));
|
2020-10-16 11:19:59 +00:00
|
|
|
add_btn(&m_discard_btn, m_continue_btn_id, dependent_presets ? "switch_presets" : "exit", Action::Discard, _L("Discard"), false);
|
2020-10-15 14:48:48 +00:00
|
|
|
add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save"));
|
|
|
|
|
|
|
|
ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24);
|
|
|
|
buttons->Add(cancel_btn, 1, wxLEFT|wxRIGHT, 5);
|
|
|
|
cancel_btn->SetFont(btn_font);
|
|
|
|
cancel_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CANCEL); });
|
2020-08-10 17:07:45 +00:00
|
|
|
|
|
|
|
m_info_line = new wxStaticText(this, wxID_ANY, "");
|
2020-08-12 11:36:26 +00:00
|
|
|
m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
|
|
|
|
m_info_line->Hide();
|
2020-07-30 14:16:56 +00:00
|
|
|
|
2020-10-15 14:48:48 +00:00
|
|
|
m_remember_choice = new wxCheckBox(this, wxID_ANY, _L("Remember my choice"));
|
|
|
|
m_remember_choice->SetValue(wxGetApp().app_config->get(m_app_config_key) != "none");
|
2020-10-15 17:10:34 +00:00
|
|
|
m_remember_choice->Bind(wxEVT_CHECKBOX, [type, this](wxCommandEvent& evt)
|
2020-10-15 14:48:48 +00:00
|
|
|
{
|
|
|
|
if (!evt.IsChecked())
|
|
|
|
return;
|
2020-10-17 12:04:05 +00:00
|
|
|
wxString preferences_item = type == Preset::TYPE_INVALID ? _L("Ask for unsaved changes when closing application") :
|
|
|
|
_L("Ask for unsaved changes when selecting new preset");
|
|
|
|
wxString msg =
|
|
|
|
_L("PrusaSlicer will remember your action.") + "\n\n" +
|
|
|
|
(type == Preset::TYPE_INVALID ?
|
|
|
|
_L("You will not be asked about the unsaved changes the next time you close PrusaSlicer.") :
|
|
|
|
_L("You will not be asked about the unsaved changes the next time you switch a preset.")) + "\n\n" +
|
|
|
|
format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto be asked about unsaved changes again."), preferences_item);
|
2020-10-15 14:48:48 +00:00
|
|
|
|
2020-10-17 12:04:05 +00:00
|
|
|
wxMessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION);
|
2020-10-15 17:10:34 +00:00
|
|
|
if (dialog.ShowModal() == wxID_CANCEL)
|
|
|
|
m_remember_choice->SetValue(false);
|
2020-10-15 14:48:48 +00:00
|
|
|
});
|
|
|
|
|
2020-07-30 14:16:56 +00:00
|
|
|
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
|
2020-08-13 13:45:16 +00:00
|
|
|
topSizer->Add(m_action_line,0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
2020-08-11 07:17:52 +00:00
|
|
|
topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
|
|
|
topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border);
|
2020-10-15 14:48:48 +00:00
|
|
|
topSizer->Add(buttons, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
|
|
|
topSizer->Add(m_remember_choice, 0, wxEXPAND | wxALL, border);
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
update(type, dependent_presets, new_selected_preset, header);
|
2020-07-30 14:16:56 +00:00
|
|
|
|
|
|
|
SetSizer(topSizer);
|
|
|
|
topSizer->SetSizeHints(this);
|
2020-08-11 07:17:52 +00:00
|
|
|
|
|
|
|
show_info_line(Action::Undef);
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
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();
|
2020-08-10 09:24:31 +00:00
|
|
|
|
|
|
|
// update an enabling of the "save/move" buttons
|
|
|
|
m_empty_selection = get_selected_options().empty();
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
|
2020-08-10 17:07:45 +00:00
|
|
|
void UnsavedChangesDialog::context_menu(wxDataViewEvent& event)
|
|
|
|
{
|
|
|
|
if (!m_has_long_strings)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wxDataViewItem item = event.GetItem();
|
|
|
|
if (!item)
|
|
|
|
{
|
|
|
|
wxPoint mouse_pos = wxGetMousePosition() - m_tree->GetScreenPosition();
|
|
|
|
wxDataViewColumn* col = nullptr;
|
|
|
|
m_tree->HitTest(mouse_pos, item, col);
|
|
|
|
|
|
|
|
if (!item)
|
|
|
|
item = m_tree->GetSelection();
|
|
|
|
|
|
|
|
if (!item)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto it = m_items_map.find(item);
|
|
|
|
if (it == m_items_map.end() || !it->second.is_long)
|
|
|
|
return;
|
|
|
|
FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val).ShowModal();
|
|
|
|
}
|
|
|
|
|
|
|
|
void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name)
|
|
|
|
{
|
|
|
|
if (action == Action::Undef && !m_has_long_strings)
|
|
|
|
m_info_line->Hide();
|
|
|
|
else {
|
|
|
|
wxString text;
|
|
|
|
if (action == Action::Undef)
|
2020-10-17 12:04:05 +00:00
|
|
|
text = _L("Some fields are too long to fit. Right mouse click reveals the full text.");
|
2020-10-15 14:48:48 +00:00
|
|
|
else if (action == Action::Discard)
|
2020-10-03 01:15:54 +00:00
|
|
|
text = _L("All modified options will be reverted.");
|
2020-08-10 17:07:45 +00:00
|
|
|
else {
|
2020-09-02 07:06:42 +00:00
|
|
|
if (preset_name.empty())
|
2020-10-17 12:04:05 +00:00
|
|
|
text = action == Action::Save ? _L("Save the selected options.") : _L("Transfer the selected options to the newly selected presets.");
|
2020-09-02 07:06:42 +00:00
|
|
|
else
|
2020-10-17 12:04:05 +00:00
|
|
|
text = format_wxstr(
|
|
|
|
action == Action::Save ?
|
|
|
|
_L("Save the selected options to preset \"%1%\".") :
|
|
|
|
_L("Transfer the selected options to the newly selected preset \"%1%\"."),
|
|
|
|
preset_name);
|
|
|
|
//text += "\n" + _L("Unselected options will be reverted.");
|
2020-08-10 17:07:45 +00:00
|
|
|
}
|
|
|
|
m_info_line->SetLabel(text);
|
|
|
|
m_info_line->Show();
|
|
|
|
}
|
|
|
|
|
|
|
|
Layout();
|
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
|
2020-10-15 14:48:48 +00:00
|
|
|
void UnsavedChangesDialog::update_config(Action action)
|
|
|
|
{
|
|
|
|
if (!m_remember_choice->GetValue())
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::string act = action == Action::Transfer ? ActTransfer :
|
|
|
|
action == Action::Discard ? ActDiscard : ActSave;
|
|
|
|
wxGetApp().app_config->set(m_app_config_key, act);
|
|
|
|
}
|
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
void UnsavedChangesDialog::close(Action action)
|
|
|
|
{
|
2020-08-10 17:07:45 +00:00
|
|
|
m_exit_action = action;
|
2020-08-05 17:25:04 +00:00
|
|
|
this->EndModal(wxID_CLOSE);
|
|
|
|
}
|
|
|
|
|
2020-10-15 17:10:34 +00:00
|
|
|
bool UnsavedChangesDialog::save(PresetCollection* dependent_presets)
|
2020-10-03 01:15:54 +00:00
|
|
|
{
|
|
|
|
names_and_types.clear();
|
|
|
|
|
|
|
|
// save one preset
|
|
|
|
if (dependent_presets) {
|
|
|
|
const Preset& preset = dependent_presets->get_edited_preset();
|
|
|
|
std::string name = preset.name;
|
|
|
|
|
|
|
|
// for system/default/external presets we should take an edited name
|
|
|
|
if (preset.is_system || preset.is_default || preset.is_external) {
|
|
|
|
SavePresetDialog save_dlg(preset.type);
|
2020-10-15 14:48:48 +00:00
|
|
|
if (save_dlg.ShowModal() != wxID_OK) {
|
|
|
|
m_exit_action = Action::Discard;
|
2020-10-15 17:10:34 +00:00
|
|
|
return false;
|
2020-10-15 14:48:48 +00:00
|
|
|
}
|
2020-10-03 01:15:54 +00:00
|
|
|
name = save_dlg.get_name();
|
|
|
|
}
|
|
|
|
|
|
|
|
names_and_types.emplace_back(make_pair(name, preset.type));
|
|
|
|
}
|
|
|
|
// save all presets
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<Preset::Type> types_for_save;
|
|
|
|
|
|
|
|
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
|
|
|
|
|
|
|
|
for (Tab* tab : wxGetApp().tabs_list)
|
|
|
|
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) {
|
|
|
|
const Preset& preset = tab->get_presets()->get_edited_preset();
|
|
|
|
if (preset.is_system || preset.is_default || preset.is_external)
|
|
|
|
types_for_save.emplace_back(preset.type);
|
|
|
|
|
|
|
|
names_and_types.emplace_back(make_pair(preset.name, preset.type));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!types_for_save.empty()) {
|
|
|
|
SavePresetDialog save_dlg(types_for_save);
|
2020-10-15 14:48:48 +00:00
|
|
|
if (save_dlg.ShowModal() != wxID_OK) {
|
|
|
|
m_exit_action = Action::Discard;
|
2020-10-15 17:10:34 +00:00
|
|
|
return false;
|
2020-10-15 14:48:48 +00:00
|
|
|
}
|
2020-10-03 01:15:54 +00:00
|
|
|
|
|
|
|
for (std::pair<std::string, Preset::Type>& nt : names_and_types) {
|
|
|
|
const std::string& name = save_dlg.get_name(nt.second);
|
|
|
|
if (!name.empty())
|
|
|
|
nt.first = name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-15 17:10:34 +00:00
|
|
|
return true;
|
2020-10-03 01:15:54 +00:00
|
|
|
}
|
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
template<class T>
|
2020-08-07 13:09:58 +00:00
|
|
|
wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config, bool is_infill = false)
|
2020-08-05 17:25:04 +00:00
|
|
|
{
|
2020-08-07 13:09:58 +00:00
|
|
|
const ConfigOptionDef& def = config.def()->options.at(opt_key);
|
|
|
|
const std::vector<std::string>& names = def.enum_labels;//ConfigOptionEnum<T>::get_enum_names();
|
2020-08-05 17:25:04 +00:00
|
|
|
T val = config.option<ConfigOptionEnum<T>>(opt_key)->value;
|
2020-08-07 13:09:58 +00:00
|
|
|
|
|
|
|
// Each infill doesn't use all list of infill declared in PrintConfig.hpp.
|
|
|
|
// So we should "convert" val to the correct one
|
|
|
|
if (is_infill) {
|
|
|
|
for (auto key_val : *def.enum_keys_map)
|
|
|
|
if ((int)key_val.second == (int)val) {
|
|
|
|
auto it = std::find(def.enum_values.begin(), def.enum_values.end(), key_val.first);
|
|
|
|
if (it == def.enum_values.end())
|
|
|
|
return "";
|
|
|
|
return from_u8(_utf8(names[it - def.enum_values.begin()]));
|
|
|
|
}
|
|
|
|
return _L("Undef");
|
|
|
|
}
|
|
|
|
return from_u8(_utf8(names[static_cast<int>(val)]));
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 14:49:06 +00:00
|
|
|
static int get_id_from_opt_key(std::string opt_key)
|
2020-08-05 17:25:04 +00:00
|
|
|
{
|
2020-08-18 14:49:06 +00:00
|
|
|
int pos = opt_key.find("#");
|
|
|
|
if (pos > 0) {
|
|
|
|
boost::erase_head(opt_key, pos + 1);
|
|
|
|
return atoi(opt_key.c_str());
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string get_pure_opt_key(std::string opt_key)
|
|
|
|
{
|
|
|
|
int pos = opt_key.find("#");
|
|
|
|
if (pos > 0)
|
|
|
|
boost::erase_tail(opt_key, opt_key.size() - pos);
|
|
|
|
return opt_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config)
|
|
|
|
{
|
2020-08-19 13:35:58 +00:00
|
|
|
int opt_idx = get_id_from_opt_key(opt_key);
|
2020-08-18 14:49:06 +00:00
|
|
|
opt_key = get_pure_opt_key(opt_key);
|
|
|
|
|
|
|
|
if (config.option(opt_key)->is_nil())
|
|
|
|
return _L("N/A");
|
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
wxString out;
|
|
|
|
|
2020-08-18 14:49:06 +00:00
|
|
|
const ConfigOptionDef* opt = config.def()->get(opt_key);
|
|
|
|
bool is_nullable = opt->nullable;
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-08-18 14:49:06 +00:00
|
|
|
switch (opt->type) {
|
2020-08-05 17:25:04 +00:00
|
|
|
case coInt:
|
|
|
|
return from_u8((boost::format("%1%") % config.opt_int(opt_key)).str());
|
|
|
|
case coInts: {
|
2020-08-18 14:49:06 +00:00
|
|
|
int val = is_nullable ?
|
|
|
|
config.opt<ConfigOptionIntsNullable>(opt_key)->get_at(opt_idx) :
|
|
|
|
config.opt<ConfigOptionInts>(opt_key)->get_at(opt_idx);
|
|
|
|
return from_u8((boost::format("%1%") % val).str());
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
case coBool:
|
|
|
|
return config.opt_bool(opt_key) ? "true" : "false";
|
|
|
|
case coBools: {
|
2020-08-18 14:49:06 +00:00
|
|
|
bool val = is_nullable ?
|
|
|
|
config.opt<ConfigOptionBoolsNullable>(opt_key)->get_at(opt_idx) :
|
|
|
|
config.opt<ConfigOptionBools>(opt_key)->get_at(opt_idx);
|
|
|
|
return val ? "true" : "false";
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
case coPercent:
|
|
|
|
return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str());
|
|
|
|
case coPercents: {
|
2020-08-18 14:49:06 +00:00
|
|
|
double val = is_nullable ?
|
|
|
|
config.opt<ConfigOptionPercentsNullable>(opt_key)->get_at(opt_idx) :
|
|
|
|
config.opt<ConfigOptionPercents>(opt_key)->get_at(opt_idx);
|
|
|
|
return from_u8((boost::format("%1%%%") % int(val)).str());
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
case coFloat:
|
|
|
|
return double_to_string(config.opt_float(opt_key));
|
|
|
|
case coFloats: {
|
2020-08-18 14:49:06 +00:00
|
|
|
double val = is_nullable ?
|
|
|
|
config.opt<ConfigOptionFloatsNullable>(opt_key)->get_at(opt_idx) :
|
|
|
|
config.opt<ConfigOptionFloats>(opt_key)->get_at(opt_idx);
|
|
|
|
return double_to_string(val);
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
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")
|
2020-08-07 13:09:58 +00:00
|
|
|
return get_string_from_enum<InfillPattern>(opt_key, config, true);
|
2020-08-05 17:25:04 +00:00
|
|
|
if (opt_key == "gcode_flavor")
|
|
|
|
return get_string_from_enum<GCodeFlavor>(opt_key, config);
|
2020-10-02 15:31:55 +00:00
|
|
|
if (opt_key == "machine_limits_usage")
|
|
|
|
return get_string_from_enum<MachineLimitsUsage>(opt_key, config);
|
2020-08-05 17:25:04 +00:00
|
|
|
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") {
|
2020-08-19 13:35:58 +00:00
|
|
|
BedShape shape(*config.option<ConfigOptionPoints>(opt_key));
|
|
|
|
return shape.get_full_name_with_params();
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
2020-08-19 13:35:58 +00:00
|
|
|
Vec2d val = config.opt<ConfigOptionPoints>(opt_key)->get_at(opt_idx);
|
|
|
|
return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str());
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2020-08-10 17:07:45 +00:00
|
|
|
wxString UnsavedChangesDialog::get_short_string(wxString full_string)
|
|
|
|
{
|
|
|
|
int max_len = 30;
|
|
|
|
if (full_string.IsEmpty() || full_string.StartsWith("#") ||
|
|
|
|
(full_string.Find("\n") == wxNOT_FOUND && full_string.Length() < max_len))
|
|
|
|
return full_string;
|
|
|
|
|
|
|
|
m_has_long_strings = true;
|
|
|
|
|
|
|
|
int n_pos = full_string.Find("\n");
|
|
|
|
if (n_pos != wxNOT_FOUND && n_pos < max_len)
|
|
|
|
max_len = n_pos;
|
|
|
|
|
|
|
|
full_string.Truncate(max_len);
|
|
|
|
return full_string + dots;
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header)
|
2020-08-05 17:25:04 +00:00
|
|
|
{
|
2020-08-13 13:45:16 +00:00
|
|
|
PresetCollection* presets = dependent_presets;
|
|
|
|
|
|
|
|
// activate buttons and labels
|
2020-08-14 16:17:16 +00:00
|
|
|
m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); });
|
2020-10-15 14:48:48 +00:00
|
|
|
if (m_transfer_btn) {
|
2020-09-02 07:06:42 +00:00
|
|
|
bool is_empty_name = type != dependent_presets->type();
|
2020-10-15 14:48:48 +00:00
|
|
|
m_transfer_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name] (wxMouseEvent& e) { show_info_line(Action::Transfer, is_empty_name ? "" : new_selected_preset); e.Skip(); });
|
2020-09-02 07:06:42 +00:00
|
|
|
}
|
2020-10-15 14:48:48 +00:00
|
|
|
m_discard_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Discard); e.Skip(); });
|
2020-08-13 13:45:16 +00:00
|
|
|
|
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
if (type == Preset::TYPE_INVALID) {
|
2020-10-17 12:04:05 +00:00
|
|
|
m_action_line->SetLabel(header + "\n" + _L("The following presets were modified:"));
|
2020-08-13 13:45:16 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-08-14 16:17:16 +00:00
|
|
|
wxString action_msg;
|
|
|
|
if (type == dependent_presets->type()) {
|
2020-10-17 12:04:05 +00:00
|
|
|
action_msg = format_wxstr(_L("Preset \"%1%\" has the following unsaved changes:"), presets->get_edited_preset().name);
|
2020-08-14 16:17:16 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-10-19 08:08:21 +00:00
|
|
|
action_msg = format_wxstr(type == Preset::TYPE_PRINTER ?
|
2020-10-17 12:04:05 +00:00
|
|
|
_L("Preset \"%1%\" is not compatible with the new printer profile and it has the following unsaved changes:") :
|
|
|
|
_L("Preset \"%1%\" is not compatible with the new print profile and it has the following unsaved changes:"),
|
|
|
|
presets->get_edited_preset().name);
|
2020-08-14 16:17:16 +00:00
|
|
|
}
|
2020-10-17 12:04:05 +00:00
|
|
|
m_action_line->SetLabel(action_msg);
|
2020-08-13 13:45:16 +00:00
|
|
|
}
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
update_tree(type, presets);
|
|
|
|
}
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* presets_)
|
|
|
|
{
|
2020-08-05 17:25:04 +00:00
|
|
|
Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
|
|
|
|
searcher.sort_options_by_opt_key();
|
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
// list of the presets with unsaved changes
|
|
|
|
std::vector<PresetCollection*> presets_list;
|
|
|
|
if (type == Preset::TYPE_INVALID)
|
|
|
|
{
|
|
|
|
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
|
|
|
|
|
|
|
|
for (Tab* tab : wxGetApp().tabs_list)
|
|
|
|
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
|
|
|
|
presets_list.emplace_back(tab->get_presets());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
presets_list.emplace_back(presets_);
|
|
|
|
|
|
|
|
// Display a dialog showing the dirty options in a human readable form.
|
|
|
|
for (PresetCollection* presets : presets_list)
|
|
|
|
{
|
|
|
|
const DynamicPrintConfig& old_config = presets->get_selected_preset().config;
|
2020-10-07 18:13:12 +00:00
|
|
|
const PrinterTechnology& old_pt = presets->get_selected_preset().printer_technology();
|
2020-08-14 16:17:16 +00:00
|
|
|
const DynamicPrintConfig& new_config = presets->get_edited_preset().config;
|
|
|
|
type = presets->type();
|
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
const std::map<wxString, std::string>& category_icon_map = wxGetApp().get_tab(type)->get_category_icon_map();
|
|
|
|
|
|
|
|
m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name), old_pt);
|
2020-08-14 16:17:16 +00:00
|
|
|
|
|
|
|
// Collect dirty options.
|
2020-08-18 14:49:06 +00:00
|
|
|
const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL);
|
|
|
|
auto dirty_options = presets->current_dirty_options(deep_compare);
|
|
|
|
auto dirty_options_ = presets->current_dirty_options();
|
|
|
|
|
|
|
|
// process changes of extruders count
|
2020-10-07 18:13:12 +00:00
|
|
|
if (type == Preset::TYPE_PRINTER && old_pt == ptFFF &&
|
2020-08-18 14:49:06 +00:00
|
|
|
old_config.opt<ConfigOptionStrings>("extruder_colour")->values.size() != new_config.opt<ConfigOptionStrings>("extruder_colour")->values.size()) {
|
|
|
|
wxString local_label = _L("Extruders count");
|
|
|
|
wxString old_val = from_u8((boost::format("%1%") % old_config.opt<ConfigOptionStrings>("extruder_colour")->values.size()).str());
|
|
|
|
wxString new_val = from_u8((boost::format("%1%") % new_config.opt<ConfigOptionStrings>("extruder_colour")->values.size()).str());
|
|
|
|
|
|
|
|
ItemData item_data = { "extruders_count", local_label, old_val, new_val, type };
|
2020-10-07 18:13:12 +00:00
|
|
|
m_items_map.emplace(m_tree_model->AddOption(type, _L("General"), _L("Capabilities"), local_label, old_val, new_val, category_icon_map.at("General")), item_data);
|
2020-08-18 14:49:06 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-09-17 13:25:05 +00:00
|
|
|
for (const std::string& opt_key : dirty_options) {
|
2020-08-14 16:17:16 +00:00
|
|
|
const Search::Option& option = searcher.get_option(opt_key);
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), type };
|
2020-08-10 17:07:45 +00:00
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
wxString old_val = get_short_string(item_data.old_val);
|
|
|
|
wxString new_val = get_short_string(item_data.new_val);
|
|
|
|
if (old_val != item_data.old_val || new_val != item_data.new_val)
|
|
|
|
item_data.is_long = true;
|
2020-08-10 17:07:45 +00:00
|
|
|
|
2020-10-07 18:13:12 +00:00
|
|
|
m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, old_val, new_val, category_icon_map.at(option.category)), item_data);
|
2020-08-14 16:17:16 +00:00
|
|
|
}
|
2020-08-05 17:25:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:17:16 +00:00
|
|
|
std::vector<std::string> UnsavedChangesDialog::get_unselected_options(Preset::Type type)
|
2020-08-12 15:33:22 +00:00
|
|
|
{
|
|
|
|
std::vector<std::string> ret;
|
|
|
|
|
2020-08-18 14:49:06 +00:00
|
|
|
for (auto item : m_items_map) {
|
|
|
|
if (item.second.opt_key == "extruders_count")
|
|
|
|
continue;
|
2020-08-14 16:17:16 +00:00
|
|
|
if (item.second.type == type && !m_tree_model->IsEnabledItem(item.first))
|
2020-08-18 14:49:06 +00:00
|
|
|
ret.emplace_back(get_pure_opt_key(item.second.opt_key));
|
|
|
|
}
|
2020-08-12 15:33:22 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2020-08-05 17:25:04 +00:00
|
|
|
|
2020-08-10 09:24:31 +00:00
|
|
|
std::vector<std::string> UnsavedChangesDialog::get_selected_options()
|
|
|
|
{
|
|
|
|
std::vector<std::string> ret;
|
|
|
|
|
2020-08-18 14:49:06 +00:00
|
|
|
for (auto item : m_items_map)
|
2020-08-10 09:24:31 +00:00
|
|
|
if (m_tree_model->IsEnabledItem(item.first))
|
2020-08-18 14:49:06 +00:00
|
|
|
ret.emplace_back(get_pure_opt_key(item.second.opt_key));
|
2020-08-10 09:24:31 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-07-30 14:16:56 +00:00
|
|
|
void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect)
|
|
|
|
{
|
2020-08-12 11:36:26 +00:00
|
|
|
int em = em_unit();
|
2020-07-30 14:16:56 +00:00
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id });
|
2020-10-15 14:48:48 +00:00
|
|
|
for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } )
|
|
|
|
if (btn) btn->msw_rescale();
|
2020-07-30 14:16:56 +00:00
|
|
|
|
2020-08-05 17:25:04 +00:00
|
|
|
const wxSize& size = wxSize(80 * em, 30 * em);
|
2020-07-30 14:16:56 +00:00
|
|
|
SetMinSize(size);
|
|
|
|
|
2020-08-12 11:36:26 +00:00
|
|
|
m_tree->GetColumn(UnsavedChangesModel::colToggle )->SetWidth(6 * em);
|
|
|
|
m_tree->GetColumn(UnsavedChangesModel::colIconText)->SetWidth(30 * em);
|
|
|
|
m_tree->GetColumn(UnsavedChangesModel::colOldValue)->SetWidth(20 * em);
|
|
|
|
m_tree->GetColumn(UnsavedChangesModel::colNewValue)->SetWidth(20 * em);
|
|
|
|
|
|
|
|
m_tree_model->Rescale();
|
|
|
|
m_tree->Refresh();
|
|
|
|
|
2020-07-30 14:16:56 +00:00
|
|
|
Fit();
|
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
void UnsavedChangesDialog::on_sys_color_changed()
|
|
|
|
{
|
2020-10-15 14:48:48 +00:00
|
|
|
for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } )
|
2020-08-12 11:36:26 +00:00
|
|
|
btn->msw_rescale();
|
2020-07-30 14:16:56 +00:00
|
|
|
// msw_rescale updates just icons, so use it
|
2020-08-12 11:36:26 +00:00
|
|
|
m_tree_model->Rescale();
|
|
|
|
m_tree->Refresh();
|
2020-07-30 14:16:56 +00:00
|
|
|
|
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-10 17:07:45 +00:00
|
|
|
//------------------------------------------
|
|
|
|
// FullCompareDialog
|
|
|
|
//------------------------------------------
|
|
|
|
|
|
|
|
FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value)
|
|
|
|
: wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
|
|
|
{
|
|
|
|
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
|
|
|
SetBackgroundColour(bgr_clr);
|
|
|
|
|
|
|
|
int border = 10;
|
|
|
|
|
|
|
|
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this);
|
|
|
|
|
|
|
|
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(2, 2, 1, 0);
|
|
|
|
grid_sizer->SetFlexibleDirection(wxBOTH);
|
|
|
|
grid_sizer->AddGrowableCol(0,1);
|
|
|
|
grid_sizer->AddGrowableCol(1,1);
|
|
|
|
grid_sizer->AddGrowableRow(1,1);
|
|
|
|
|
|
|
|
auto add_header = [grid_sizer, border, this](wxString label) {
|
|
|
|
wxStaticText* text = new wxStaticText(this, wxID_ANY, label);
|
2020-08-12 11:36:26 +00:00
|
|
|
text->SetFont(this->GetFont().Bold());
|
2020-08-10 17:07:45 +00:00
|
|
|
grid_sizer->Add(text, 0, wxALL, border);
|
|
|
|
};
|
|
|
|
|
|
|
|
auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) {
|
2020-08-11 08:37:08 +00:00
|
|
|
wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH);
|
2020-08-12 15:33:22 +00:00
|
|
|
text->SetStyle(0, label.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont()));
|
2020-08-10 17:07:45 +00:00
|
|
|
grid_sizer->Add(text, 1, wxALL | wxEXPAND, border);
|
|
|
|
};
|
|
|
|
|
|
|
|
add_header(_L("Old value"));
|
|
|
|
add_header(_L("New value"));
|
|
|
|
add_value(old_value);
|
|
|
|
add_value(new_value, true);
|
|
|
|
|
|
|
|
sizer->Add(grid_sizer, 1, wxEXPAND);
|
|
|
|
|
|
|
|
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK);
|
|
|
|
|
|
|
|
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
|
|
|
|
topSizer->Add(sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
|
|
|
topSizer->Add(buttons, 0, wxEXPAND | wxALL, border);
|
|
|
|
|
|
|
|
SetSizer(topSizer);
|
|
|
|
topSizer->SetSizeHints(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-07-30 14:16:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Slic3r::GUI
|