mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 22:42:37 +00:00
The grey header strip carrying the Settings/Old Value/New Value titles was added to the tab panel without wxEXPAND, so it stayed at the width its own sizer fitted it to. The panel, the scrolled window and every category and value row below it are added with wxEXPAND and do follow the dialog width, so as soon as the dialog came out wider than the table, the header stopped short and left a bare strip of panel to the right of New Value. The dialog takes its width from the widest child of the main sizer, which includes the button row. In the Transfer/Discard/Save variant three Choice buttons, each with a hard FromDIP(100) minimum, plus the gaps, the checkbox and the margins come within a few pixels of the table width, and at higher display scaling they overrun it. Give the header the panel width and send the surplus to a stretch spacer after the last column, so the columns stay put: they are hard minimum sizes on both sides, and the rows below leave that same area empty, so header and values remain aligned. The Settings column drops to proportion 0 so it does not absorb the surplus and slide the other two titles out of alignment. The header is unchanged when the dialog is not wider than its table. Verified on Linux with a release build: with the dialog widened, the header goes from ending at x=511 to spanning the full 749, and the pixel difference between the two builds is confined to the strip that was previously bare. At normal width the two builds render identically. Not verified on Windows.
2307 lines
91 KiB
C++
2307 lines
91 KiB
C++
#include "UnsavedChangesDialog.hpp"
|
|
|
|
#include <cstddef>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/optional.hpp>
|
|
|
|
#include <wx/tokenzr.h>
|
|
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
#include "libslic3r/PresetBundle.hpp"
|
|
#include "libslic3r/Color.hpp"
|
|
#include "format.hpp"
|
|
#include "ConfigValueFormatter.hpp"
|
|
#include "GUI_App.hpp"
|
|
#include "Plater.hpp"
|
|
#include "Tab.hpp"
|
|
#include "ExtraRenderers.hpp"
|
|
#include "wxExtensions.hpp"
|
|
#include "SavePresetDialog.hpp"
|
|
#include "MainFrame.hpp"
|
|
#include "MsgDialog.hpp"
|
|
|
|
#include "PresetComboBoxes.hpp"
|
|
#include "Widgets/CheckBox.hpp"
|
|
#include "Widgets/DialogButtons.hpp"
|
|
#include "Widgets/HyperLink.hpp"
|
|
|
|
using boost::optional;
|
|
|
|
#ifdef __linux__
|
|
#define wxLinux true
|
|
#else
|
|
#define wxLinux false
|
|
#endif
|
|
|
|
namespace Slic3r {
|
|
|
|
namespace GUI {
|
|
|
|
wxDEFINE_EVENT(EVT_DIFF_DIALOG_TRANSFER, SimpleEvent);
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// ModelNode: a node inside DiffModel
|
|
// ----------------------------------------------------------------------------
|
|
|
|
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, "blank_16" },
|
|
{Preset::TYPE_PRINTER, "printer" },
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
static std::string def_text_color()
|
|
{
|
|
wxColour def_colour = wxGetApp().get_label_clr_default();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
|
return encode_color(ColorRGB(def_colour.Red(), def_colour.Green(), def_colour.Blue()));
|
|
}
|
|
static std::string grey = "#808080";
|
|
static std::string orange = "#ed6b21";
|
|
|
|
static void color_string(wxString& str, const std::string& color)
|
|
{
|
|
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
|
|
str = from_u8((boost::format("<span color=\"%1%\">%2%</span>") % color % into_u8(str)).str());
|
|
#endif
|
|
}
|
|
|
|
static void make_string_bold(wxString& str)
|
|
{
|
|
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
|
|
str = from_u8((boost::format("<b>%1%</b>") % into_u8(str)).str());
|
|
#endif
|
|
}
|
|
|
|
// preset(root) node
|
|
ModelNode::ModelNode(Preset::Type preset_type, wxWindow* parent_win, const wxString& text, const std::string& icon_name) :
|
|
m_parent_win(parent_win),
|
|
m_parent(nullptr),
|
|
m_preset_type(preset_type),
|
|
m_icon_name(icon_name),
|
|
m_text(text)
|
|
{
|
|
UpdateIcons();
|
|
}
|
|
|
|
// category node
|
|
ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name) :
|
|
m_parent_win(parent->m_parent_win),
|
|
m_parent(parent),
|
|
m_icon_name(icon_name),
|
|
m_text(text)
|
|
{
|
|
UpdateIcons();
|
|
}
|
|
|
|
// group node
|
|
ModelNode::ModelNode(ModelNode* parent, const wxString& text) :
|
|
m_parent_win(parent->m_parent_win),
|
|
m_parent(parent),
|
|
m_icon_name("node_dot"),
|
|
m_text(text)
|
|
{
|
|
UpdateIcons();
|
|
}
|
|
|
|
#ifdef __linux__
|
|
wxIcon ModelNode::get_bitmap(const wxString& color)
|
|
#else
|
|
wxBitmap ModelNode::get_bitmap(const wxString& color)
|
|
#endif // __linux__
|
|
{
|
|
/* 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;
|
|
ColorRGB rgb;
|
|
decode_color(into_u8(color), rgb);
|
|
// there is no need to scale created solid bitmap
|
|
#ifndef __linux__
|
|
return bmp_cache.mksolid(icon_width, icon_height, rgb, true);
|
|
#else
|
|
wxIcon icon;
|
|
icon.CopyFromBitmap(bmp_cache.mksolid(icon_width, icon_height, rgb, true));
|
|
return icon;
|
|
#endif // __linux__
|
|
}
|
|
|
|
// 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_icon_name("empty"),
|
|
m_text(text),
|
|
m_old_value(old_value),
|
|
m_new_value(new_value),
|
|
m_container(false)
|
|
{
|
|
// check if old/new_value is color
|
|
if (m_old_color.IsEmpty()) {
|
|
if (!m_new_color.IsEmpty())
|
|
m_old_value = _L("Undefined");
|
|
}
|
|
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("Undefined");
|
|
}
|
|
else {
|
|
m_new_color_bmp = get_bitmap(m_new_color);
|
|
m_new_value.Clear();
|
|
}
|
|
|
|
// "color" strings
|
|
color_string(m_old_value, def_text_color());
|
|
color_string(m_new_value, orange);
|
|
|
|
UpdateIcons();
|
|
}
|
|
|
|
void ModelNode::UpdateEnabling()
|
|
{
|
|
auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to)
|
|
{
|
|
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
|
|
std::string old_val = into_u8(str);
|
|
boost::replace_all(old_val, clr_from, clr_to);
|
|
str = from_u8(old_val);
|
|
#endif
|
|
};
|
|
|
|
if (!m_toggle) {
|
|
change_text_color(m_text, def_text_color(), grey);
|
|
change_text_color(m_old_value, def_text_color(), grey);
|
|
change_text_color(m_new_value, orange,grey);
|
|
}
|
|
else {
|
|
change_text_color(m_text, grey, def_text_color());
|
|
change_text_color(m_old_value, grey, def_text_color());
|
|
change_text_color(m_new_value, grey, orange);
|
|
}
|
|
// update icons for the colors
|
|
UpdateIcons();
|
|
}
|
|
|
|
void ModelNode::UpdateIcons()
|
|
{
|
|
// update icons for the colors, if any exists
|
|
if (!m_old_color.IsEmpty())
|
|
m_old_color_bmp = get_bitmap(m_toggle ? m_old_color : wxString::FromUTF8(grey.c_str()));
|
|
if (!m_new_color.IsEmpty())
|
|
m_new_color_bmp = get_bitmap(m_toggle ? m_new_color : wxString::FromUTF8(grey.c_str()));
|
|
|
|
// 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__
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// DiffModel
|
|
// ----------------------------------------------------------------------------
|
|
|
|
DiffModel::DiffModel(wxWindow* parent) :
|
|
m_parent_win(parent)
|
|
{
|
|
}
|
|
|
|
wxDataViewItem DiffModel::AddPreset(Preset::Type type, wxString preset_name, PrinterTechnology pt)
|
|
{
|
|
// "color" strings
|
|
color_string(preset_name, def_text_color());
|
|
make_string_bold(preset_name);
|
|
|
|
auto preset = new ModelNode(type, m_parent_win, preset_name, get_icon_name(type, pt));
|
|
m_preset_nodes.emplace_back(preset);
|
|
|
|
wxDataViewItem child((void*)preset);
|
|
wxDataViewItem parent(nullptr);
|
|
|
|
ItemAdded(parent, child);
|
|
return child;
|
|
}
|
|
|
|
ModelNode* DiffModel::AddOption(ModelNode* group_node, wxString option_name, wxString old_value, wxString new_value)
|
|
{
|
|
group_node->Append(std::make_unique<ModelNode>(group_node, option_name, old_value, new_value));
|
|
ModelNode* option = group_node->GetChildren().back().get();
|
|
wxDataViewItem group_item = wxDataViewItem((void*)group_node);
|
|
ItemAdded(group_item, wxDataViewItem((void*)option));
|
|
|
|
m_ctrl->Expand(group_item);
|
|
return option;
|
|
}
|
|
|
|
ModelNode* DiffModel::AddOptionWithGroup(ModelNode* category_node, wxString group_name, wxString option_name, wxString old_value, wxString new_value)
|
|
{
|
|
category_node->Append(std::make_unique<ModelNode>(category_node, group_name));
|
|
ModelNode* group_node = category_node->GetChildren().back().get();
|
|
ItemAdded(wxDataViewItem((void*)category_node), wxDataViewItem((void*)group_node));
|
|
|
|
return AddOption(group_node, option_name, old_value, new_value);
|
|
}
|
|
|
|
ModelNode* DiffModel::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)
|
|
{
|
|
preset_node->Append(std::make_unique<ModelNode>(preset_node, category_name, category_icon_name));
|
|
ModelNode* category_node = preset_node->GetChildren().back().get();
|
|
ItemAdded(wxDataViewItem((void*)preset_node), wxDataViewItem((void*)category_node));
|
|
|
|
return AddOptionWithGroup(category_node, group_name, option_name, old_value, new_value);
|
|
}
|
|
|
|
wxDataViewItem DiffModel::AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name,
|
|
wxString old_value, wxString new_value, const std::string category_icon_name)
|
|
{
|
|
// "color" strings
|
|
color_string(category_name, def_text_color());
|
|
color_string(group_name, def_text_color());
|
|
color_string(option_name, def_text_color());
|
|
|
|
// "make" strings bold
|
|
make_string_bold(category_name);
|
|
make_string_bold(group_name);
|
|
|
|
// add items
|
|
for (std::unique_ptr<ModelNode>& preset : m_preset_nodes)
|
|
if (preset->type() == type)
|
|
{
|
|
for (std::unique_ptr<ModelNode> &category : preset->GetChildren())
|
|
if (category->text() == category_name)
|
|
{
|
|
for (std::unique_ptr<ModelNode> &group : category->GetChildren())
|
|
if (group->text() == group_name)
|
|
return wxDataViewItem((void*)AddOption(group.get(), option_name, old_value, new_value));
|
|
|
|
return wxDataViewItem((void*)AddOptionWithGroup(category.get(), group_name, option_name, old_value, new_value));
|
|
}
|
|
|
|
return wxDataViewItem((void*)AddOptionWithGroupAndCategory(preset.get(), category_name, group_name, option_name, old_value, new_value, category_icon_name));
|
|
}
|
|
|
|
return wxDataViewItem(nullptr);
|
|
}
|
|
|
|
static void update_children(ModelNode* parent)
|
|
{
|
|
if (parent->IsContainer()) {
|
|
bool toggle = parent->IsToggled();
|
|
for (std::unique_ptr<ModelNode> &child : parent->GetChildren()) {
|
|
child->Toggle(toggle);
|
|
child->UpdateEnabling();
|
|
update_children(child.get());
|
|
}
|
|
}
|
|
}
|
|
|
|
static void update_parents(ModelNode* node)
|
|
{
|
|
ModelNode* parent = node->GetParent();
|
|
if (parent) {
|
|
bool toggle = false;
|
|
for (std::unique_ptr<ModelNode> &child : parent->GetChildren()) {
|
|
if (child->IsToggled()) {
|
|
toggle = true;
|
|
break;
|
|
}
|
|
}
|
|
parent->Toggle(toggle);
|
|
parent->UpdateEnabling();
|
|
update_parents(parent);
|
|
}
|
|
}
|
|
|
|
void DiffModel::UpdateItemEnabling(wxDataViewItem item)
|
|
{
|
|
assert(item.IsOk());
|
|
ModelNode* node = static_cast<ModelNode*>(item.GetID());
|
|
node->UpdateEnabling();
|
|
|
|
update_children(node);
|
|
update_parents(node);
|
|
}
|
|
|
|
bool DiffModel::IsEnabledItem(const wxDataViewItem& item)
|
|
{
|
|
assert(item.IsOk());
|
|
ModelNode* node = static_cast<ModelNode*>(item.GetID());
|
|
return node->IsToggled();
|
|
}
|
|
|
|
void DiffModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const
|
|
{
|
|
assert(item.IsOk());
|
|
|
|
ModelNode* node = static_cast<ModelNode*>(item.GetID());
|
|
switch (col)
|
|
{
|
|
case colToggle:
|
|
variant = node->m_toggle;
|
|
break;
|
|
#ifdef __linux__
|
|
case colIconText:
|
|
variant << wxDataViewIconText(node->m_text, node->m_icon);
|
|
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;
|
|
#else
|
|
case colIconText:
|
|
variant << DataViewBitmapText(node->m_text, node->m_icon);
|
|
break;
|
|
case colOldValue:
|
|
variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp);
|
|
break;
|
|
case colNewValue:
|
|
variant << DataViewBitmapText(node->m_new_value, node->m_new_color_bmp);
|
|
break;
|
|
#endif //__linux__
|
|
|
|
default:
|
|
wxLogError("DiffModel::GetValue: wrong column %d", col);
|
|
}
|
|
}
|
|
|
|
bool DiffModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col)
|
|
{
|
|
assert(item.IsOk());
|
|
|
|
ModelNode* node = static_cast<ModelNode*>(item.GetID());
|
|
switch (col)
|
|
{
|
|
case colToggle:
|
|
node->m_toggle = variant.GetBool();
|
|
return true;
|
|
#ifdef __linux__
|
|
case colIconText: {
|
|
wxDataViewIconText data;
|
|
data << variant;
|
|
node->m_icon = data.GetIcon();
|
|
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; }
|
|
#else
|
|
case colIconText: {
|
|
DataViewBitmapText data;
|
|
data << variant;
|
|
node->m_icon = data.GetBitmap();
|
|
node->m_text = data.GetText();
|
|
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; }
|
|
#endif //__linux__
|
|
default:
|
|
wxLogError("DiffModel::SetValue: wrong column");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DiffModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const
|
|
{
|
|
assert(item.IsOk());
|
|
if (col == colToggle)
|
|
return true;
|
|
|
|
// disable unchecked nodes
|
|
return (static_cast<ModelNode*>(item.GetID()))->IsToggled();
|
|
}
|
|
|
|
wxDataViewItem DiffModel::GetParent(const wxDataViewItem& item) const
|
|
{
|
|
// the invisible root node has no parent
|
|
if (!item.IsOk())
|
|
return wxDataViewItem(nullptr);
|
|
|
|
ModelNode* node = static_cast<ModelNode*>(item.GetID());
|
|
|
|
if (node->IsRoot())
|
|
return wxDataViewItem(nullptr);
|
|
|
|
return wxDataViewItem((void*)node->GetParent());
|
|
}
|
|
|
|
bool DiffModel::IsContainer(const wxDataViewItem& item) const
|
|
{
|
|
// the invisble root node can have children
|
|
if (!item.IsOk())
|
|
return true;
|
|
|
|
ModelNode* node = static_cast<ModelNode*>(item.GetID());
|
|
return node->IsContainer();
|
|
}
|
|
|
|
unsigned int DiffModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const
|
|
{
|
|
ModelNode* parent_node = (ModelNode*)parent.GetID();
|
|
|
|
const ModelNodePtrArray& children = parent_node ? parent_node->GetChildren() : m_preset_nodes;
|
|
for (const std::unique_ptr<ModelNode>& child : children)
|
|
array.Add(wxDataViewItem((void*)child.get()));
|
|
|
|
return array.Count();
|
|
}
|
|
|
|
|
|
wxString DiffModel::GetColumnType(unsigned int col) const
|
|
{
|
|
switch (col)
|
|
{
|
|
case colToggle:
|
|
return "bool";
|
|
case colIconText:
|
|
case colOldValue:
|
|
case colNewValue:
|
|
default:
|
|
return "DataViewBitmapText";//"string";
|
|
}
|
|
}
|
|
|
|
static void rescale_children(ModelNode* parent)
|
|
{
|
|
if (parent->IsContainer()) {
|
|
for (std::unique_ptr<ModelNode> &child : parent->GetChildren()) {
|
|
child->UpdateIcons();
|
|
rescale_children(child.get());
|
|
}
|
|
}
|
|
}
|
|
|
|
void DiffModel::Rescale()
|
|
{
|
|
for (std::unique_ptr<ModelNode> &node : m_preset_nodes) {
|
|
node->UpdateIcons();
|
|
rescale_children(node.get());
|
|
}
|
|
}
|
|
|
|
wxDataViewItem DiffModel::Delete(const wxDataViewItem& item)
|
|
{
|
|
auto ret_item = wxDataViewItem(nullptr);
|
|
ModelNode* node = static_cast<ModelNode*>(item.GetID());
|
|
if (!node) // happens if item.IsOk()==false
|
|
return ret_item;
|
|
|
|
// first remove the node from the parent's array of children;
|
|
// NOTE: m_preset_nodes is only a vector of _pointers_
|
|
// thus removing the node from it doesn't result in freeing it
|
|
ModelNodePtrArray& children = node->GetChildren();
|
|
// Delete all children
|
|
while (!children.empty())
|
|
Delete(wxDataViewItem(children.back().get()));
|
|
|
|
auto node_parent = node->GetParent();
|
|
wxDataViewItem parent(node_parent);
|
|
|
|
ModelNodePtrArray& parents_children = node_parent ? node_parent->GetChildren() : m_preset_nodes;
|
|
auto it = find_if(parents_children.begin(), parents_children.end(),
|
|
[node](std::unique_ptr<ModelNode>& child) { return child.get() == node; });
|
|
assert(it != parents_children.end());
|
|
it = parents_children.erase(it);
|
|
|
|
if (it != parents_children.end())
|
|
ret_item = wxDataViewItem(it->get());
|
|
|
|
// set m_container to FALSE if parent has no child
|
|
if (node_parent) {
|
|
#ifndef __WXGTK__
|
|
if (node_parent->GetChildCount() == 0)
|
|
node_parent->m_container = false;
|
|
#endif //__WXGTK__
|
|
ret_item = parent;
|
|
}
|
|
|
|
// notify control
|
|
ItemDeleted(parent, item);
|
|
return ret_item;
|
|
}
|
|
|
|
void DiffModel::Clear()
|
|
{
|
|
while (!m_preset_nodes.empty())
|
|
Delete(wxDataViewItem(m_preset_nodes.back().get()));
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// DiffViewCtrl
|
|
// ----------------------------------------------------------------------------
|
|
|
|
DiffViewCtrl::DiffViewCtrl(wxWindow* parent, wxSize size)
|
|
: wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, size, wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES
|
|
#ifdef _WIN32
|
|
| wxBORDER_SIMPLE
|
|
#endif
|
|
),
|
|
m_em_unit(em_unit(parent))
|
|
{
|
|
wxGetApp().UpdateDVCDarkUI(this);
|
|
|
|
model = new DiffModel(parent);
|
|
this->AssociateModel(model);
|
|
model->SetAssociatedControl(this);
|
|
|
|
this->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &DiffViewCtrl::context_menu, this);
|
|
this->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, &DiffViewCtrl::context_menu, this);
|
|
this->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &DiffViewCtrl::item_value_changed, this);
|
|
}
|
|
|
|
DiffViewCtrl::~DiffViewCtrl() {
|
|
this->AssociateModel(nullptr);
|
|
delete model;
|
|
}
|
|
|
|
void DiffViewCtrl::AppendBmpTextColumn(const wxString& label, unsigned model_column, int width, bool set_expander/* = false*/)
|
|
{
|
|
m_columns_width.emplace(this->GetColumnCount(), width);
|
|
#ifdef __linux__
|
|
wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer();
|
|
#ifdef SUPPORTS_MARKUP
|
|
rd->EnableMarkup(true);
|
|
#endif
|
|
wxDataViewColumn* column = new wxDataViewColumn(label, rd, model_column, width * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT);
|
|
#else
|
|
wxDataViewColumn* column = new wxDataViewColumn(label, new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), model_column, width * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE);
|
|
#endif //__linux__
|
|
this->AppendColumn(column);
|
|
if (set_expander)
|
|
this->SetExpanderColumn(column);
|
|
|
|
}
|
|
|
|
void DiffViewCtrl::AppendToggleColumn_(const wxString& label, unsigned model_column, int width)
|
|
{
|
|
m_columns_width.emplace(this->GetColumnCount(), width);
|
|
AppendToggleColumn(label, model_column, wxDATAVIEW_CELL_ACTIVATABLE, width * m_em_unit);
|
|
}
|
|
|
|
void DiffViewCtrl::Rescale(int em /*= 0*/)
|
|
{
|
|
if (em > 0) {
|
|
for (auto item : m_columns_width)
|
|
GetColumn(item.first)->SetWidth(item.second * em);
|
|
m_em_unit = em;
|
|
}
|
|
|
|
model->Rescale();
|
|
Refresh();
|
|
}
|
|
|
|
|
|
void DiffViewCtrl::Append( const std::string& opt_key, Preset::Type type,
|
|
wxString category_name, wxString group_name, wxString option_name,
|
|
wxString old_value, wxString new_value, const std::string category_icon_name)
|
|
{
|
|
ItemData item_data = { opt_key, option_name, old_value, new_value, type };
|
|
|
|
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;
|
|
|
|
m_items_map.emplace(model->AddOption(type, category_name, group_name, option_name, old_val, new_val, category_icon_name), item_data);
|
|
|
|
}
|
|
|
|
void DiffViewCtrl::Clear()
|
|
{
|
|
model->Clear();
|
|
m_items_map.clear();
|
|
}
|
|
|
|
wxString DiffViewCtrl::get_short_string(wxString full_string)
|
|
{
|
|
size_t 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 < (int)max_len)
|
|
max_len = n_pos;
|
|
|
|
full_string.Truncate(max_len);
|
|
return full_string + dots;
|
|
}
|
|
|
|
void DiffViewCtrl::context_menu(wxDataViewEvent& event)
|
|
{
|
|
if (!m_has_long_strings)
|
|
return;
|
|
|
|
wxDataViewItem item = event.GetItem();
|
|
if (!item) {
|
|
wxPoint mouse_pos = wxGetMousePosition() - this->GetScreenPosition();
|
|
wxDataViewColumn* col = nullptr;
|
|
this->HitTest(mouse_pos, item, col);
|
|
|
|
if (!item)
|
|
item = this->GetSelection();
|
|
|
|
if (!item)
|
|
return;
|
|
}
|
|
|
|
auto it = m_items_map.find(item);
|
|
if (it == m_items_map.end() || !it->second.is_long)
|
|
return;
|
|
|
|
size_t column_cnt = this->GetColumnCount();
|
|
const wxString old_value_header = this->GetColumn(column_cnt - 2)->GetTitle();
|
|
const wxString new_value_header = this->GetColumn(column_cnt - 1)->GetTitle();
|
|
FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val,
|
|
old_value_header, new_value_header).ShowModal();
|
|
|
|
#ifdef __WXOSX__
|
|
wxWindow* parent = this->GetParent();
|
|
if (parent && parent->IsShown()) {
|
|
// if this dialog is shown it have to be Hide and show again to be placed on the very Top of windows
|
|
parent->Hide();
|
|
parent->Show();
|
|
}
|
|
#endif // __WXOSX__
|
|
}
|
|
|
|
void DiffViewCtrl::item_value_changed(wxDataViewEvent& event)
|
|
{
|
|
if (event.GetColumn() != DiffModel::colToggle)
|
|
return;
|
|
|
|
wxDataViewItem item = event.GetItem();
|
|
|
|
model->UpdateItemEnabling(item);
|
|
Refresh();
|
|
|
|
// update an enabling of the "save/move" buttons
|
|
m_empty_selection = selected_options().empty();
|
|
}
|
|
|
|
bool DiffViewCtrl::has_unselected_options()
|
|
{
|
|
for (auto item : m_items_map)
|
|
if (!model->IsEnabledItem(item.first))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> DiffViewCtrl::options(Preset::Type type, bool selected)
|
|
{
|
|
std::vector<std::string> ret;
|
|
|
|
for (auto item : m_items_map) {
|
|
if (item.second.type == type && model->IsEnabledItem(item.first) == selected)
|
|
ret.emplace_back(get_pure_opt_key(item.second.opt_key));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> DiffViewCtrl::selected_options()
|
|
{
|
|
std::vector<std::string> ret;
|
|
|
|
for (auto item : m_items_map)
|
|
if (model->IsEnabledItem(item.first))
|
|
ret.emplace_back(get_pure_opt_key(item.second.opt_key));
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//------------------------------------------
|
|
// UnsavedChangesDialog
|
|
//------------------------------------------
|
|
|
|
static std::string none{"none"};
|
|
#define UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE wxSize(FromDIP(490), FromDIP(374))
|
|
#define UNSAVE_CHANGE_DIALOG_ACTION_LINE_SIZE wxSize(FromDIP(490), -1)
|
|
#define UNSAVE_CHANGE_DIALOG_FIRST_VALUE_WIDTH FromDIP(190)
|
|
#define UNSAVE_CHANGE_DIALOG_VALUE_WIDTH FromDIP(150)
|
|
#define UNSAVE_CHANGE_DIALOG_ITEM_HEIGHT FromDIP(24)
|
|
#define UNSAVE_CHANGE_DIALOG_BUTTON_SIZE wxSize(FromDIP(70), FromDIP(24))
|
|
|
|
#define THUMB_COLOR wxColor(196, 196, 196)
|
|
#define GREY900 wxColour(38, 46, 48)
|
|
#define GREY700 wxColour(107,107,107)
|
|
#define GREY400 wxColour(206,206,206)
|
|
#define GREY300 wxColour(238,238,238)
|
|
#define GREY200 wxColour(248,248,248)
|
|
|
|
|
|
UnsavedChangesDialog::UnsavedChangesDialog(const wxString &caption, const wxString &header, const std::string &app_config_key, int act_buttons)
|
|
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe),
|
|
wxID_ANY,
|
|
caption + ": " + _L("unsaved changes"),
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
wxCAPTION | wxCLOSE_BOX)
|
|
, m_app_config_key(app_config_key)
|
|
, m_buttons(act_buttons)
|
|
{
|
|
build(Preset::TYPE_INVALID, nullptr, "", header);
|
|
this->CenterOnScreen();
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
UnsavedChangesDialog::UnsavedChangesDialog(const wxString &caption, const wxString &header, DynamicConfig *config, int from, int to, bool left_to_right, NozzleVolumeType nozzle)
|
|
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe),
|
|
wxID_ANY,
|
|
caption,
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
wxCAPTION | wxCLOSE_BOX)
|
|
, m_buttons(ActionButtons::SAVE | ActionButtons::DONT_SAVE)
|
|
{
|
|
SyncExtruderParams params { config, from, to, left_to_right, nozzle };
|
|
build(Preset::TYPE_PRINT, reinterpret_cast<PresetCollection*>(¶ms), "SyncExtruderParams", header);
|
|
this->CenterOnScreen();
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection *dependent_presets, const std::string &new_selected_preset, bool no_transfer)
|
|
: m_new_selected_preset_name(new_selected_preset)
|
|
, DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe),
|
|
wxID_ANY,
|
|
_L("Transfer or discard changes"),
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
wxCAPTION | wxCLOSE_BOX)
|
|
{
|
|
if (new_selected_preset.empty() || no_transfer)
|
|
m_buttons &= ~ActionButtons::TRANSFER;
|
|
build(type, dependent_presets, new_selected_preset);
|
|
this->CenterOnScreen();
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
|
|
inline int UnsavedChangesDialog::ShowModal()
|
|
{
|
|
auto choise_key = "save_preset_choise";
|
|
auto choise = wxGetApp().app_config->get(choise_key);
|
|
long result = 0;
|
|
if ((m_buttons & REMEMBER_CHOISE) && !choise.empty() && wxString(choise).ToLong(&result) && (1 << result) & (m_buttons | DONT_SAVE)) {
|
|
m_exit_action = Action(result);
|
|
return 0;
|
|
}
|
|
int r = DPIDialog::ShowModal();
|
|
if (r != wxID_CANCEL && dynamic_cast<::CheckBox*>(FindWindowById(wxID_APPLY))->GetValue()) {
|
|
wxGetApp().app_config->set(choise_key, std::to_string(int(m_exit_action)));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_presets, const std::string &new_selected_preset, const wxString &header)
|
|
{
|
|
SetBackgroundColour(*wxWHITE);
|
|
|
|
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
|
|
|
m_top_line = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
|
m_top_line->SetBackgroundColour(wxColour(166, 169, 170));
|
|
|
|
m_sizer_main->Add(m_top_line, 0, wxEXPAND, 0);
|
|
|
|
m_sizer_main->Add(0, 0, 0, wxTOP, 20);
|
|
|
|
m_action_line = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, UNSAVE_CHANGE_DIALOG_ACTION_LINE_SIZE, 0);
|
|
m_action_line->SetFont(::Label::Body_13);
|
|
m_action_line->SetForegroundColour(GREY900);
|
|
m_action_line->Wrap(-1);
|
|
m_sizer_main->Add(m_action_line, 0, wxLEFT | wxRIGHT, 20);
|
|
|
|
m_sizer_main->Add(0, 0, 0, wxTOP, 12);
|
|
|
|
SyncExtruderParams *params = nullptr;
|
|
if (new_selected_preset == "SyncExtruderParams") {
|
|
params = reinterpret_cast<SyncExtruderParams *>(dependent_presets);
|
|
dependent_presets = nullptr;
|
|
}
|
|
|
|
m_panel_tab = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE.x, -1), wxTAB_TRAVERSAL);
|
|
m_panel_tab->SetBackgroundColour(GREY200);
|
|
wxBoxSizer *m_sizer_tab = new wxBoxSizer(wxVERTICAL);
|
|
|
|
m_table_top = new wxPanel(m_panel_tab, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
|
m_table_top->SetBackgroundColour(wxColour(107, 107, 107));
|
|
|
|
wxBoxSizer *m_sizer_top = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
// m_sizer_top->Add(0, 0, 0, wxLEFT, UNSAVE_CHANGE_DIALOG_FIRST_VALUE_WIDTH);
|
|
auto m_panel_temp = new wxPanel(m_table_top, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_FIRST_VALUE_WIDTH, -1), wxTAB_TRAVERSAL);
|
|
wxBoxSizer *top_title_temp_v = new wxBoxSizer(wxVERTICAL);
|
|
top_title_temp_v->SetMinSize(wxSize(UNSAVE_CHANGE_DIALOG_VALUE_WIDTH, -1));
|
|
wxBoxSizer *top_title_temp_h = new wxBoxSizer(wxHORIZONTAL);
|
|
static_temp_title = new wxStaticText(m_panel_temp, wxID_ANY, _L("Settings"), wxDefaultPosition, wxDefaultSize, 0);
|
|
static_temp_title->SetFont(::Label::Body_13);
|
|
static_temp_title->Wrap(-1);
|
|
static_temp_title->SetForegroundColour(*wxWHITE);
|
|
top_title_temp_h->Add(static_temp_title, 0, wxALIGN_CENTER | wxBOTTOM | wxTOP, 5);
|
|
top_title_temp_v->Add(top_title_temp_h, 1, wxALIGN_CENTER, 0);
|
|
m_panel_temp->SetSizer(top_title_temp_v);
|
|
m_panel_temp->Layout();
|
|
m_sizer_top->Add(m_panel_temp, 0, wxALIGN_CENTER, 0);
|
|
|
|
title_block_middle = new wxPanel(m_table_top, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
|
title_block_middle->SetBackgroundColour(wxColour(172, 172, 172));
|
|
|
|
m_sizer_top->Add(title_block_middle, 0, wxBOTTOM | wxEXPAND | wxTOP, 2);
|
|
auto m_panel_oldv = new wxPanel( m_table_top, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_VALUE_WIDTH,-1), wxTAB_TRAVERSAL );
|
|
wxBoxSizer *top_title_oldv = new wxBoxSizer(wxVERTICAL);
|
|
wxBoxSizer *top_title_oldv_h = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
std::string ucd_pt = wxGetApp().preset_bundle->printers.get_edited_preset().get_printer_type(wxGetApp().preset_bundle);
|
|
static_oldv_title = new wxStaticText(m_panel_oldv, wxID_ANY, params ? _L(DevPrinterConfigUtil::get_toolhead_display_name(ucd_pt, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::SentenceCase)) + ": " + get_nozzle_volume_type_name(params->nozzle) : _L("Old Value"), wxDefaultPosition, wxDefaultSize, 0);
|
|
static_oldv_title->SetFont(::Label::Body_13);
|
|
static_oldv_title->Wrap(-1);
|
|
static_oldv_title->SetForegroundColour(params && params->left_to_right ? wxGetApp().get_label_clr_modified() : *wxWHITE);
|
|
top_title_oldv_h->Add(static_oldv_title, 0, wxALIGN_CENTER | wxBOTTOM | wxTOP, 5);
|
|
top_title_oldv->Add(top_title_oldv_h, 1, wxALIGN_CENTER, 0);
|
|
m_panel_oldv->SetSizer(top_title_oldv);
|
|
m_panel_oldv->Layout();
|
|
m_sizer_top->Add(m_panel_oldv, 0, wxALIGN_CENTER, 0);
|
|
|
|
title_block_right = new wxPanel(m_table_top, wxID_ANY, wxDefaultPosition, wxSize(1, -1), wxTAB_TRAVERSAL);
|
|
title_block_right->SetBackgroundColour(wxColour(172, 172, 172));
|
|
|
|
m_sizer_top->Add(title_block_right, 0, wxBOTTOM | wxEXPAND | wxTOP, 2);
|
|
|
|
auto m_panel_newv = new wxPanel( m_table_top, wxID_ANY, wxDefaultPosition, wxSize( UNSAVE_CHANGE_DIALOG_VALUE_WIDTH,-1 ), wxTAB_TRAVERSAL );
|
|
wxBoxSizer *top_title_newv = new wxBoxSizer(wxVERTICAL);
|
|
wxBoxSizer *top_title_newv_h = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
static_newv_title = new wxStaticText(m_panel_newv, wxID_ANY, params ? _L(DevPrinterConfigUtil::get_toolhead_display_name(ucd_pt, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::SentenceCase)) + ": " + get_nozzle_volume_type_name(params->nozzle) : _L("New Value"),
|
|
wxDefaultPosition, wxDefaultSize, 0);
|
|
static_newv_title->SetFont(::Label::Body_13);
|
|
static_newv_title->Wrap(-1);
|
|
static_newv_title->SetForegroundColour(params && !params->left_to_right ? wxGetApp().get_label_clr_modified() : *wxWHITE);
|
|
|
|
top_title_newv_h->Add(static_newv_title, 0, wxALIGN_CENTER | wxBOTTOM | wxTOP, 5);
|
|
|
|
top_title_newv->Add(top_title_newv_h, 1, wxALIGN_CENTER, 0);
|
|
|
|
m_panel_newv->SetSizer(top_title_newv);
|
|
m_panel_newv->Layout();
|
|
m_sizer_top->Add(m_panel_newv, 0, wxALIGN_CENTER, 0);
|
|
//m_sizer_top->Add(top_title_newv, 1, wxALIGN_CENTER, 0);
|
|
m_sizer_top->AddStretchSpacer();
|
|
|
|
m_table_top->SetSizer(m_sizer_top);
|
|
m_table_top->Layout();
|
|
m_sizer_top->Fit(m_table_top);
|
|
m_sizer_tab->Add(m_table_top, 0, wxEXPAND, 0);
|
|
|
|
m_scrolledWindow = new wxScrolledWindow(m_panel_tab, wxID_ANY, wxDefaultPosition, UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE, wxNO_BORDER|wxVSCROLL);
|
|
m_scrolledWindow->SetScrollRate(0, 5);
|
|
m_scrolledWindow->SetBackgroundColour(GREY200);
|
|
m_sizer_bottom = new wxBoxSizer(wxVERTICAL);
|
|
m_sizer_bottom->Add(m_scrolledWindow, 1, wxEXPAND, 0);
|
|
m_sizer_tab->Add(m_sizer_bottom, 0, wxEXPAND, 0);
|
|
|
|
m_panel_tab->SetSizer(m_sizer_tab);
|
|
m_panel_tab->Layout();
|
|
m_sizer_tab->Fit(m_panel_tab);
|
|
m_sizer_main->Add(m_panel_tab, 0, wxEXPAND | wxLEFT | wxRIGHT, 20);
|
|
|
|
m_sizer_main->Add(0, 0, 0, wxTOP, 9);
|
|
|
|
/* m_info_line = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 44), 0);
|
|
m_info_line->SetFont(::Label::Body_13);
|
|
m_info_line->Wrap(-1);
|
|
m_info_line->SetForegroundColour(wxColour(255, 111, 0));
|
|
m_sizer_main->Add(m_info_line, 0, wxLEFT | wxRIGHT, 20);*/
|
|
|
|
wxBoxSizer *m_sizer_button = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
auto checkbox_sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
auto checkbox = new ::CheckBox(this, wxID_APPLY);
|
|
checkbox_sizer->Add(checkbox, 0, wxALL | wxALIGN_CENTER, FromDIP(2));
|
|
|
|
auto checkbox_text = new wxStaticText(this, wxID_ANY, _L("Remember my choice."), wxDefaultPosition, wxDefaultSize, 0);
|
|
checkbox_sizer->Add(checkbox_text, 0, wxALL | wxALIGN_CENTER, FromDIP(2));
|
|
checkbox_text->SetFont(::Label::Body_13);
|
|
checkbox_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#323A3D")));
|
|
m_sizer_button->Add(checkbox_sizer, 0, wxLEFT, FromDIP(22));
|
|
checkbox_sizer->Show(bool(m_buttons & REMEMBER_CHOISE));
|
|
|
|
if (dependent_presets != nullptr) {
|
|
auto wiki = new HyperLink(this, _L("Help"), "https://www.orcaslicer.com/wiki/transfer_discard_changes");
|
|
m_sizer_button->Add(wiki, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(22));
|
|
}
|
|
|
|
m_sizer_button->Add(0, 0, 1, 0, 0);
|
|
|
|
// Add Buttons
|
|
auto add_btn = [this, m_sizer_button, dependent_presets](Button **btn, int &btn_id, const std::string &icon_name, Action close_act, const wxString &label,
|
|
bool focus, bool process_enable = true) {
|
|
*btn = new Button(this, _L(label));
|
|
|
|
(*btn)->SetStyle(focus ? ButtonStyle::Confirm : ButtonStyle::Regular, ButtonType::Choice);
|
|
|
|
(*btn)->Bind(wxEVT_BUTTON, [this, close_act, dependent_presets](wxEvent &) {
|
|
bool save_names_and_types = close_act == Action::Save || (close_act == Action::Transfer && ActionButtons::KEEP & m_buttons);
|
|
if (save_names_and_types && !save(dependent_presets, close_act == Action::Save)) return;
|
|
close(close_act);
|
|
});
|
|
|
|
// if (process_enable) (*btn)->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent &evt) { evt.Enable(m_tree->has_selection()); });
|
|
(*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &e) {
|
|
show_info_line(Action::Undef);
|
|
e.Skip();
|
|
});
|
|
|
|
m_sizer_button->Add(*btn, 0, wxLEFT, FromDIP(ButtonProps::ChoiceButtonGap()));
|
|
};
|
|
|
|
// "Transfer" / "Keep" button
|
|
if (ActionButtons::TRANSFER & m_buttons) {
|
|
const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets();
|
|
if (dependent_presets && switched_presets && (type == dependent_presets->type() ?
|
|
dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() :
|
|
switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology()))
|
|
add_btn(&m_transfer_btn, m_move_btn_id, "menu_paste", Action::Transfer, switched_presets->get_edited_preset().name == new_selected_preset ? _L("Transfer") : _L("Transfer"), true);
|
|
}
|
|
if (!m_transfer_btn && (ActionButtons::KEEP & m_buttons))
|
|
add_btn(&m_transfer_btn, m_move_btn_id, "menu_paste", Action::Transfer, _L("Transfer"), true);
|
|
|
|
bool is_copy = new_selected_preset == "SyncExtruderParams";
|
|
{ // "Don't save" / "Discard" button
|
|
std::string btn_icon = (ActionButtons::DONT_SAVE & m_buttons) ? "" : (dependent_presets || (ActionButtons::KEEP & m_buttons)) ? "blank_16" : "exit";
|
|
wxString btn_label = (ActionButtons::DONT_SAVE & m_buttons) ? (is_copy ? _L("No") : _L("Don't save")) : _L("Discard");
|
|
add_btn(&m_discard_btn, m_continue_btn_id, btn_icon, Action::Discard, btn_label, false);
|
|
}
|
|
|
|
// "Save" button
|
|
if (ActionButtons::SAVE & m_buttons) add_btn(&m_save_btn, m_save_btn_id, "save", is_copy ? Action::Transfer : Action::Save, is_copy ? _L("Yes") : _L("Save"), false);
|
|
|
|
if (dependent_presets != nullptr) {
|
|
const wxString previous_profile = from_u8(dependent_presets->get_edited_preset().name);
|
|
const wxString new_profile = new_selected_preset.empty() ? _L("the new profile") : from_u8(new_selected_preset);
|
|
|
|
if (m_discard_btn) {
|
|
m_discard_btn->SetToolTip(format_wxstr(
|
|
_L("Switch to\n\"%1%\"\ndiscarding any changes made in\n\"%2%\"."),
|
|
new_profile,
|
|
previous_profile));
|
|
}
|
|
|
|
if (m_transfer_btn) {
|
|
m_transfer_btn->SetToolTip(format_wxstr(
|
|
_L("All \"New Value\" settings modified in\n\"%1%\"\nwill be transferred to\n\"%2%\"."),
|
|
previous_profile,
|
|
new_profile));
|
|
}
|
|
|
|
if (m_save_btn) {
|
|
m_save_btn->SetToolTip(format_wxstr(
|
|
_L("All \"New Value\" settings are saved in\n\"%1%\"\nand \"%2%\" will open without any changes."),
|
|
previous_profile,
|
|
new_profile));
|
|
}
|
|
}
|
|
|
|
/* 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);*/
|
|
/* m_cancel_btn = new Button(this, _L("Cancel"));
|
|
m_cancel_btn->SetTextColor(wxColour(107, 107, 107));
|
|
m_cancel_btn->Bind(wxEVT_LEFT_DOWN, [this](wxEvent &) { this->EndModal(wxID_CANCEL); });
|
|
m_cancel_btn->SetMinSize(UNSAVE_CHANGE_DIALOG_BUTTON_SIZE);
|
|
m_cancel_btn->SetCornerRadius(12);
|
|
m_sizer_button->Add(m_cancel_btn, 0, wxLEFT, 5);
|
|
m_sizer_button->Add(0,0,0,wxRIGHT,20);*/
|
|
|
|
if (!m_app_config_key.empty()) {}
|
|
|
|
m_sizer_button->Add(0, 0, 0, wxRIGHT, 20);
|
|
m_sizer_main->Add(m_sizer_button, 0, wxEXPAND | wxTOP, 6);
|
|
m_sizer_main->Add(0, 0, 1, wxTOP, 18);
|
|
|
|
if (params) {
|
|
if (params->left_to_right)
|
|
update_tree(type, params->config, params->from, params->to);
|
|
else
|
|
update_tree(type, params->config, params->to, params->from);
|
|
m_action_line->SetLabel(header);
|
|
m_action_line->Wrap(UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE.x);
|
|
update_list(params);
|
|
} else {
|
|
update(type, dependent_presets, new_selected_preset, header);
|
|
}
|
|
//SetSizer(topSizer);
|
|
//topSizer->SetSizeHints(this);
|
|
|
|
show_info_line(Action::Undef);
|
|
|
|
SetSizerAndFit(m_sizer_main);
|
|
Layout();
|
|
Fit();
|
|
// Centre(wxBOTH);
|
|
}
|
|
|
|
void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name)
|
|
{
|
|
return;
|
|
if (action == Action::Undef && !m_has_long_strings)
|
|
m_info_line->SetLabel(wxEmptyString);
|
|
else {
|
|
wxString text;
|
|
if (action == Action::Undef)
|
|
text = _L("Click the right mouse button to display the full text.");
|
|
else if (action == Action::Discard)
|
|
text = ActionButtons::DONT_SAVE & m_buttons ? _L("No changes will be saved.") :_L("All changes will be discarded.");
|
|
else {
|
|
if (preset_name.empty())
|
|
text = action == Action::Save ? _L("Save the selected options.") :
|
|
ActionButtons::KEEP & m_buttons ? _L("Keep the selected options.") :
|
|
_L("Transfer the selected options to the newly selected preset.");
|
|
else
|
|
text = format_wxstr(
|
|
action == Action::Save ?
|
|
_L("Save the selected options to preset \n\"%1%\".") :
|
|
_L("Transfer the selected options to the newly selected preset \n\"%1%\"."),
|
|
preset_name);
|
|
//text += "\n" + _L("Unselected options will be reverted.");
|
|
}
|
|
m_info_line->SetLabel(text);
|
|
m_info_line->Show();
|
|
}
|
|
|
|
//Layout();
|
|
//Refresh();
|
|
}
|
|
|
|
void UnsavedChangesDialog::close(Action action)
|
|
{
|
|
if (action == Action::Transfer) {
|
|
check_option_valid();
|
|
}
|
|
m_exit_action = action;
|
|
this->EndModal(wxID_CLOSE);
|
|
}
|
|
|
|
bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_save_preset_dialog/* = true*/)
|
|
{
|
|
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
|
|
//BBS: add project embedded preset logic and refine is_external
|
|
bool save_to_project = false;
|
|
if (!preset.can_overwrite()) {
|
|
//if (preset.is_system || preset.is_default || preset.is_external) {
|
|
SavePresetDialog save_dlg(this, preset.type);
|
|
if (save_dlg.ShowModal() != wxID_OK) {
|
|
m_exit_action = Action::Discard;
|
|
return false;
|
|
}
|
|
name = save_dlg.get_name();
|
|
save_to_project = save_dlg.get_save_to_project_selection(preset.type);
|
|
}
|
|
|
|
//BBS: add project embedded preset relate logic
|
|
PresetData preset_data(name, preset.type, save_to_project);
|
|
names_and_types.emplace_back(preset_data);
|
|
//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();
|
|
//BBS: add project embedded preset logic and refine is_external
|
|
if (!preset.can_overwrite())
|
|
//if (preset.is_system || preset.is_default || preset.is_external)
|
|
types_for_save.emplace_back(preset.type);
|
|
|
|
//BBS: add project embedded preset relate logic
|
|
PresetData preset_data(preset.name, preset.type, preset.is_project_embedded);
|
|
names_and_types.emplace_back(preset_data);
|
|
//names_and_types.emplace_back(make_pair(preset.name, preset.type));
|
|
}
|
|
|
|
|
|
if (show_save_preset_dialog && !types_for_save.empty()) {
|
|
SavePresetDialog save_dlg(this, types_for_save);
|
|
if (save_dlg.ShowModal() != wxID_OK) {
|
|
m_exit_action = Action::Discard;
|
|
return false;
|
|
}
|
|
|
|
//BBS: add project embedded preset relate logic
|
|
for (PresetData& nt : names_and_types) {
|
|
const std::string& name = save_dlg.get_name(nt.type);
|
|
if (!name.empty())
|
|
nt.name = name;
|
|
nt.save_to_project = save_dlg.get_save_to_project_selection(nt.type);
|
|
}
|
|
//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;
|
|
//}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// BBS
|
|
#if 0
|
|
static size_t get_id_from_opt_key(std::string opt_key)
|
|
{
|
|
int pos = opt_key.find("#");
|
|
if (pos > 0) {
|
|
boost::erase_head(opt_key, pos + 1);
|
|
return static_cast<size_t>(atoi(opt_key.c_str()));
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header)
|
|
{
|
|
PresetCollection* presets = dependent_presets;
|
|
|
|
// activate buttons and labels
|
|
if (m_save_btn)
|
|
m_save_btn->Bind(wxEVT_ENTER_WINDOW, [this, presets](wxMouseEvent &e) {
|
|
show_info_line(Action::Save, presets ? presets->get_selected_preset().name : "");
|
|
e.Skip();
|
|
});
|
|
|
|
|
|
if (m_transfer_btn) {
|
|
bool is_empty_name = dependent_presets && type != dependent_presets->type();
|
|
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(); });
|
|
}
|
|
if (m_discard_btn)
|
|
m_discard_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Discard); e.Skip(); });
|
|
|
|
if (type == Preset::TYPE_INVALID) {
|
|
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
|
|
int presets_cnt = 0;
|
|
for (Tab* tab : wxGetApp().tabs_list)
|
|
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
|
|
presets_cnt++;
|
|
/*m_action_line->SetLabel((header.IsEmpty() ? "" : header + "\n\n") +
|
|
_L_PLURAL("The following preset was modified",
|
|
"The following presets were modified", presets_cnt));*/
|
|
}
|
|
else {
|
|
wxString action_msg;
|
|
if (type == dependent_presets->type()) {
|
|
action_msg = format_wxstr(_L("Preset \"%1%\" contains the following unsaved changes:"), presets->get_edited_preset().name);
|
|
}
|
|
else {
|
|
action_msg = format_wxstr(type == Preset::TYPE_PRINTER ?
|
|
_L("Preset \"%1%\" is not compatible with the new printer profile and it contains the following unsaved changes:") :
|
|
_L("Preset \"%1%\" is not compatible with the new process profile and it contains the following unsaved changes:"),
|
|
presets->get_edited_preset().name);
|
|
}
|
|
//m_action_line->SetLabel(action_msg);
|
|
}
|
|
|
|
wxString action_msg;
|
|
if (dependent_presets) {
|
|
action_msg = format_wxstr(_L("You have changed some settings of preset \"%1%\"."), dependent_presets->get_edited_preset().name);
|
|
if (!m_transfer_btn) {
|
|
action_msg += _L("\nYou can save or discard the preset values you have modified.");
|
|
} else {
|
|
action_msg += _L("\nYou can save or discard the preset values you have modified, or choose to transfer the values you have modified to the new preset.");
|
|
}
|
|
} else {
|
|
action_msg = _L("You have previously modified your settings.");
|
|
if (m_transfer_btn)
|
|
action_msg += _L("\nYou can discard the preset values you have modified, or choose to transfer the modified values to the new project");
|
|
else
|
|
action_msg += _L("\nYou can save or discard the preset values you have modified.");
|
|
}
|
|
|
|
m_action_line->SetLabel(action_msg);
|
|
m_action_line->Wrap(UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE.x);
|
|
|
|
update_tree(type, presets);
|
|
update_list();
|
|
}
|
|
|
|
void UnsavedChangesDialog::update_list(SyncExtruderParams *params)
|
|
{
|
|
if (!m_scrolledWindow) {
|
|
Layout();
|
|
Fit();
|
|
return;
|
|
}
|
|
|
|
std::map<wxString, std::vector<PresetItem>> class_g_list;
|
|
std::map<wxString, std::vector<wxString>> class_c_list;
|
|
std::vector<wxString> category_list;
|
|
|
|
// group
|
|
for (auto i = 0; i < m_presetitems.size(); i++) {
|
|
auto name = m_presetitems[i].category_name + ":" + m_presetitems[i].group_name;
|
|
if (class_g_list.count(name) <= 0) {
|
|
std::vector<PresetItem> vp;
|
|
vp.push_back(m_presetitems[i]);
|
|
class_g_list.emplace(name, vp);
|
|
} else {
|
|
//for (auto iter = class_g_list.begin(); iter != class_g_list.end(); iter++) iter->second.push_back(m_presetitems[i]);
|
|
class_g_list[name].push_back(m_presetitems[i]);
|
|
}
|
|
}
|
|
|
|
// category
|
|
for (auto i = 0; i < m_presetitems.size(); i++) {
|
|
auto name = m_presetitems[i].category_name + ":" + m_presetitems[i].group_name;
|
|
if (class_c_list.count(m_presetitems[i].category_name) <= 0) {
|
|
std::vector<wxString> vp;
|
|
vp.push_back(name);
|
|
class_c_list.emplace(m_presetitems[i].category_name, vp);
|
|
category_list.push_back(m_presetitems[i].category_name);
|
|
} else {
|
|
/*for (auto iter = class_c_list.begin(); iter != class_c_list.end(); iter++)
|
|
iter->second.push_back(m_presetitems[i].group_name);*/
|
|
//class_c_list[m_presetitems[i].category_name].push_back(m_presetitems[i].group_name);
|
|
std::vector<wxString>::iterator it;
|
|
it = find(class_c_list[m_presetitems[i].category_name].begin(), class_c_list[m_presetitems[i].category_name].end(), name);
|
|
if (it == class_c_list[m_presetitems[i].category_name].end()) {
|
|
class_c_list[m_presetitems[i].category_name].push_back(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
auto m_listsizer = new wxBoxSizer(wxVERTICAL);
|
|
for (auto category : category_list) {
|
|
auto iter = class_c_list.find(category);
|
|
//category
|
|
auto panel_category = new wxPanel(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxSize(-1, UNSAVE_CHANGE_DIALOG_ITEM_HEIGHT), wxTAB_TRAVERSAL);
|
|
panel_category->SetBackgroundColour(GREY300);
|
|
|
|
wxBoxSizer *sizer_category = new wxBoxSizer(wxHORIZONTAL);
|
|
wxBoxSizer *sizer_category_v = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
auto text_category = new wxStaticText(panel_category, wxID_ANY, iter->first, wxDefaultPosition, wxSize(-1, -1), 0);
|
|
text_category->SetFont(::Label::Head_13);
|
|
text_category->SetForegroundColour(GREY900);
|
|
text_category->Wrap(-1);
|
|
|
|
sizer_category_v->Add(text_category, 0, wxALIGN_CENTER | wxLEFT, 23);
|
|
|
|
sizer_category->Add(sizer_category_v, 1, wxEXPAND, 0);
|
|
|
|
panel_category->SetSizer(sizer_category);
|
|
panel_category->Layout();
|
|
m_listsizer->Add(panel_category, 0, wxEXPAND, 0);
|
|
|
|
/*auto item_line = new wxStaticLine(list, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
|
|
item_line->SetForegroundColour(wxColour(206, 206, 206));
|
|
item_line->SetBackgroundColour(wxColour(206, 206, 206));
|
|
sizer_list->Add(item_line, 0, wxALL, 0); */
|
|
|
|
// isset group
|
|
for (auto i = 0; i < iter->second.size(); i++) {
|
|
auto gname = iter->second[i];
|
|
for (auto g = 0; g < class_g_list[gname].size(); g++) {
|
|
|
|
//first group
|
|
if (g == 0) {
|
|
auto panel_item = new wxWindow(m_scrolledWindow, -1, wxDefaultPosition, wxSize(-1, UNSAVE_CHANGE_DIALOG_ITEM_HEIGHT));
|
|
panel_item->SetBackgroundColour(GREY200);
|
|
|
|
wxBoxSizer *sizer_item = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
auto panel_left = new wxPanel(panel_item, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_FIRST_VALUE_WIDTH, -1), wxTAB_TRAVERSAL);
|
|
panel_left->SetBackgroundColour(GREY200);
|
|
|
|
wxBoxSizer *sizer_left_v = new wxBoxSizer(wxVERTICAL);
|
|
|
|
auto text_left = new wxStaticText(panel_left, wxID_ANY, class_g_list[gname][0].group_name, wxDefaultPosition, wxSize(-1, -1), 0);
|
|
text_left->SetFont(::Label::Head_13);
|
|
text_left->Wrap(-1);
|
|
text_left->SetForegroundColour(GREY700);
|
|
|
|
sizer_left_v->Add(text_left, 0, wxLEFT, 37);
|
|
|
|
panel_left->SetSizer(sizer_left_v);
|
|
panel_left->Layout();
|
|
sizer_item->Add(panel_left, 0, wxALIGN_CENTER, 0);
|
|
|
|
panel_item->SetSizer(sizer_item);
|
|
panel_item->Layout();
|
|
m_listsizer->Add(panel_item, 0, wxEXPAND, 0);
|
|
}
|
|
|
|
auto data = class_g_list[gname][g];
|
|
|
|
auto panel_item = new wxWindow(m_scrolledWindow, -1, wxDefaultPosition, wxSize(-1, UNSAVE_CHANGE_DIALOG_ITEM_HEIGHT));
|
|
panel_item->SetBackgroundColour(GREY200);
|
|
|
|
wxBoxSizer *sizer_item = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
auto panel_left = new wxPanel(panel_item, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_FIRST_VALUE_WIDTH , -1), wxTAB_TRAVERSAL);
|
|
panel_left->SetBackgroundColour(GREY200);
|
|
|
|
wxBoxSizer *sizer_left_v = new wxBoxSizer(wxVERTICAL);
|
|
|
|
auto text_left = new wxStaticText(panel_left, wxID_ANY, data.option_name, wxDefaultPosition, wxSize(-1, -1), 0);
|
|
text_left->SetFont(::Label::Body_13);
|
|
text_left->Wrap(-1);
|
|
text_left->SetForegroundColour(GREY700);
|
|
|
|
sizer_left_v->Add(text_left, 0, wxLEFT, 51 );
|
|
|
|
panel_left->SetSizer(sizer_left_v);
|
|
panel_left->Layout();
|
|
sizer_item->Add(panel_left, 0, wxALIGN_CENTER, 0);
|
|
|
|
auto panel_oldv = new wxPanel(panel_item, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_VALUE_WIDTH, -1), wxTAB_TRAVERSAL);
|
|
wxBoxSizer *sizer_old_v = new wxBoxSizer(wxVERTICAL);
|
|
|
|
|
|
data.old_value = subreplace(data.old_value.ToStdString(), "\n", " ");
|
|
auto text_oldv = new wxStaticText(panel_oldv, wxID_ANY, data.old_value, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
|
text_oldv->SetFont(::Label::Body_13);
|
|
text_oldv->Wrap(-1);
|
|
text_oldv->SetForegroundColour(params && params->left_to_right ? wxGetApp().get_label_clr_modified() : GREY700);
|
|
sizer_old_v->Add(text_oldv, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5);
|
|
|
|
panel_oldv->SetSizer(sizer_old_v);
|
|
panel_oldv->Layout();
|
|
sizer_item->Add(panel_oldv, 0, wxALIGN_CENTER, 0);
|
|
|
|
auto panel_newv = new wxPanel(panel_item, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_VALUE_WIDTH, -1), wxTAB_TRAVERSAL);
|
|
wxBoxSizer *sizer_new_v = new wxBoxSizer(wxVERTICAL);
|
|
|
|
data.new_value = subreplace(data.new_value.ToStdString(), "\n", " ");
|
|
auto text_newv = new wxStaticText(panel_newv, wxID_ANY, data.new_value, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
|
text_newv->SetFont(::Label::Body_13);
|
|
text_newv->Wrap(-1);
|
|
text_newv->SetForegroundColour(params && !params->left_to_right ? wxGetApp().get_label_clr_modified() : GREY700);
|
|
|
|
sizer_new_v->Add(text_newv, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5);
|
|
|
|
panel_newv->SetSizer(sizer_new_v);
|
|
panel_newv->Layout();
|
|
sizer_item->Add(panel_newv, 0, wxALIGN_CENTER, 0);
|
|
|
|
panel_item->SetSizer(sizer_item);
|
|
panel_item->Layout();
|
|
m_listsizer->Add(panel_item, 0, wxEXPAND, 0);
|
|
|
|
|
|
|
|
////if (g == class_g_list[gname].size() - 1) {
|
|
// auto item_line = new wxStaticLine(list, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
|
|
// item_line->SetForegroundColour(wxColour(206, 206, 206));
|
|
// item_line->SetBackgroundColour(wxColour(206, 206, 206));
|
|
// sizer_list->Add(item_line, 0, wxALL, 0);
|
|
////}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_scrolledWindow->SetSizer(m_listsizer);
|
|
m_scrolledWindow->Layout();
|
|
/*wxSize text_size = m_action_line->GetTextExtent(m_action_line->GetLabel());
|
|
int width = UNSAVE_CHANGE_DIALOG_ACTION_LINE_SIZE.GetWidth();
|
|
// +2: Ensure that there is at least one line and that the content contains '\n'
|
|
int rows = int(text_size.GetWidth() / width) + 2;
|
|
int height = rows * text_size.GetHeight();
|
|
m_action_line->SetMinSize(wxSize(width, height));
|
|
m_action_line->Wrap(UNSAVE_CHANGE_DIALOG_ACTION_LINE_SIZE.GetWidth());*/
|
|
Layout();
|
|
Fit();
|
|
}
|
|
|
|
std::string UnsavedChangesDialog::subreplace(std::string resource_str, std::string sub_str, std::string new_str)
|
|
{
|
|
std::string dst_str = resource_str;
|
|
std::string::size_type pos = 0;
|
|
while ((pos = dst_str.find(sub_str)) != std::string::npos)
|
|
{
|
|
dst_str.replace(pos, sub_str.length(), new_str);
|
|
}
|
|
return dst_str;
|
|
}
|
|
|
|
void UnsavedChangesDialog::update_tree(Preset::Type type, DynamicConfig * config, int from, int to)
|
|
{
|
|
Search::OptionsSearcher &searcher = wxGetApp().sidebar().get_searcher();
|
|
searcher.sort_options_by_key();
|
|
|
|
for (const std::string &opt_key : config->keys()) {
|
|
int variant_index = -2;
|
|
Search::Option option = searcher.get_option(opt_key, type, variant_index);
|
|
if (variant_index == -2) {
|
|
// Orca: Every transferred setting must remain visible even when it is absent from the search index.
|
|
const ConfigOptionDef* def = print_config_def.get(opt_key);
|
|
const std::string label = def ? (def->full_label.empty() ? def->label : def->full_label) : std::string();
|
|
option.label_local = (label.empty() ? from_u8(opt_key) : _L(label)).ToStdWstring();
|
|
option.category_local = (def && !def->category.empty() ?
|
|
Tab::translate_category(from_u8(def->category), type) : _L("Others")).ToStdWstring();
|
|
}
|
|
auto category = option.category_local;
|
|
auto opt = dynamic_cast<ConfigOptionVectorBase*>(config->option(opt_key));
|
|
std::string value_from = opt->vserialize()[from];
|
|
std::string value_to = opt->vserialize()[to];
|
|
PresetItem pi = {type, opt_key, category, option.group_local, option.label_local, into_u8(value_from), into_u8(value_to)};
|
|
m_presetitems.push_back(pi);
|
|
}
|
|
}
|
|
|
|
void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* presets_)
|
|
{
|
|
Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
|
|
searcher.sort_options_by_key();
|
|
|
|
// 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_);
|
|
|
|
const bool multiple_extruders = wxGetApp().preset_bundle->get_printer_extruder_count() > 1;
|
|
|
|
// 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;
|
|
const PrinterTechnology& old_pt = presets->get_selected_preset().printer_technology();
|
|
const DynamicPrintConfig& new_config = presets->get_edited_preset().config;
|
|
type = presets->type();
|
|
|
|
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);
|
|
|
|
// Collect dirty options.
|
|
const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_PRINT || type == Preset::TYPE_FILAMENT || type == Preset::TYPE_SLA_MATERIAL);
|
|
auto dirty_options = presets->current_dirty_options(deep_compare);
|
|
|
|
// process changes of extruders count
|
|
if (type == Preset::TYPE_PRINTER && old_pt == ptFFF &&
|
|
old_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size() != new_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()) {
|
|
wxString local_label = _L("Extruder count");
|
|
wxString old_val = from_u8((boost::format("%1%") % old_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()).str());
|
|
wxString new_val = from_u8((boost::format("%1%") % new_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()).str());
|
|
|
|
//BBS: the page "General" changed to "Basic information" instead
|
|
//m_tree->Append("extruders_count", type, _L("General"), _L("Capabilities"), local_label, old_val, new_val, category_icon_map.at("Basic information"));
|
|
//m_tree->Append("extruders_count", type, _L("General"), _L("Capabilities"), local_label, old_val, new_val, category_icon_map.at("General"));
|
|
|
|
if (old_val != new_val) {
|
|
PresetItem pi = {type, "extruders_count", _L("General"), _L("Capabilities"), local_label, old_val, new_val};
|
|
m_presetitems.push_back(pi);
|
|
}
|
|
}
|
|
|
|
auto variant_key = Preset::get_iot_type_string(type) + "_extruder_variant";
|
|
auto id_key = Preset::get_iot_type_string(type) + "_extruder_id";
|
|
// Orca: Dirty indices belong to the edited config, which may contain newly added variants.
|
|
auto extruder_variant = dynamic_cast<ConfigOptionStrings const *>(new_config.option(variant_key));
|
|
auto extruder_id = dynamic_cast<ConfigOptionInts const *>(new_config.option(id_key));
|
|
|
|
for (const std::string& opt_key : dirty_options) {
|
|
int variant_index = -2;
|
|
const Search::Option &option = searcher.get_option(opt_key, type, variant_index);
|
|
if (variant_index == -2) {
|
|
// When founded option isn't the correct one.
|
|
// It can be for dirty_options: "default_print_profile", "printer_model", "printer_settings_id",
|
|
// because of they don't exist in searcher
|
|
continue;
|
|
}
|
|
wxString category = option.category_local;
|
|
wxString label = option.label_local;
|
|
if (type == Preset::TYPE_PRINTER && variant_index >= 0 &&
|
|
printer_options_with_variant_2.count(get_pure_opt_key(opt_key)) > 0) {
|
|
// Orca: silent_mode is obsolete on import, but its option and two-column UI still exist.
|
|
// Keep mode labels for configs that explicitly enable it; omit them in the default single-mode UI.
|
|
if (new_config.opt_bool("silent_mode"))
|
|
label += " (" + (variant_index % 2 == 0 ? _L("Normal") : _L("Silent")) + ")";
|
|
variant_index /= 2;
|
|
}
|
|
if (variant_index >= 0 && extruder_variant && variant_index < extruder_variant->size()) {
|
|
// Orca: Match the untranslated category and use the same extruder names as the printer tabs.
|
|
if (option.category.compare(0, 9, L"Extruder ") == 0)
|
|
category = _L("Extruder");
|
|
wxString variant_label = L(extruder_variant->values[variant_index]);
|
|
// Orca: An extruder name only disambiguates variants on printers with multiple extruders.
|
|
if (multiple_extruders && extruder_id && variant_index < extruder_id->size() && extruder_id->values[variant_index] > 0) {
|
|
const wxString extruder_name = Tab::translate_category(
|
|
wxString::Format("Extruder %d", extruder_id->values[variant_index]), Preset::TYPE_PRINTER);
|
|
variant_label = extruder_name + " (" + variant_label + ")";
|
|
}
|
|
category = variant_label + ": " + category;
|
|
}
|
|
|
|
/*m_tree->Append(opt_key, type, option.category_local, option.group_local, option.label_local,
|
|
get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), category_icon_map.at(option.category));*/
|
|
|
|
|
|
//PresetItem pi = {opt_key, type, 1983};
|
|
//m_presetitems.push_back()
|
|
PresetItem pi = {type, opt_key, category, option.group_local, label, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)};
|
|
m_presetitems.push_back(pi);
|
|
|
|
}
|
|
}
|
|
|
|
// Revert sort of searcher back
|
|
searcher.sort_options_by_label();
|
|
}
|
|
|
|
void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect)
|
|
{
|
|
int em = em_unit();
|
|
|
|
//msw_buttons_rescale(this, em, { wxID_CANCEL, m_move_btn_id, m_continue_btn_id });
|
|
for (auto btn : {m_transfer_btn, m_discard_btn, m_save_btn})
|
|
if (btn) btn->Rescale();
|
|
|
|
//m_cancel_btn->SetMinSize(UNSAVE_CHANGE_DIALOG_BUTTON_SIZE);
|
|
const wxSize& size = wxSize(70 * em, 30 * em);
|
|
SetMinSize(size);
|
|
//m_tree->Rescale(em);
|
|
|
|
Fit();
|
|
Refresh();
|
|
}
|
|
|
|
void UnsavedChangesDialog::on_sys_color_changed()
|
|
{
|
|
//for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } )
|
|
//btn->msw_rescale();
|
|
// msw_rescale updates just icons, so use it
|
|
//m_tree->Rescale();
|
|
|
|
Refresh();
|
|
}
|
|
|
|
bool UnsavedChangesDialog::check_option_valid()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
//------------------------------------------
|
|
// FullCompareDialog
|
|
//------------------------------------------
|
|
|
|
FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value,
|
|
const wxString& old_value_header, const wxString& new_value_header)
|
|
: wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
|
{
|
|
SetBackgroundColour(*wxWHITE);
|
|
|
|
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);
|
|
text->SetFont(this->GetFont().Bold());
|
|
grid_sizer->Add(text, 0, wxALL, border);
|
|
};
|
|
|
|
add_header(old_value_header);
|
|
add_header(new_value_header);
|
|
|
|
auto get_set_from_val = [](wxString str) {
|
|
if (str.Find("\n") == wxNOT_FOUND)
|
|
str.Replace(" ", "\n");
|
|
|
|
std::set<wxString> str_set;
|
|
|
|
wxStringTokenizer strings(str, "\n");
|
|
while (strings.HasMoreTokens())
|
|
str_set.emplace(strings.GetNextToken());
|
|
|
|
return str_set;
|
|
};
|
|
|
|
std::set<wxString> old_set = get_set_from_val(old_value);
|
|
std::set<wxString> new_set = get_set_from_val(new_value);
|
|
std::set<wxString> old_new_diff_set;
|
|
std::set<wxString> new_old_diff_set;
|
|
|
|
std::set_difference(old_set.begin(), old_set.end(), new_set.begin(), new_set.end(), std::inserter(old_new_diff_set, old_new_diff_set.begin()));
|
|
std::set_difference(new_set.begin(), new_set.end(), old_set.begin(), old_set.end(), std::inserter(new_old_diff_set, new_old_diff_set.begin()));
|
|
|
|
auto add_value = [grid_sizer, border, this](wxString label, const std::set<wxString>& diff_set, bool is_colored = false) {
|
|
wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(400, 400), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_DEFAULT | wxTE_RICH);
|
|
wxGetApp().UpdateDarkUI(text);
|
|
text->SetStyle(0, label.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont()));
|
|
|
|
for (const wxString& str : diff_set) {
|
|
int pos = label.First(str);
|
|
if (pos == wxNOT_FOUND)
|
|
continue;
|
|
text->SetStyle(pos, pos + (int)str.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont().Bold()));
|
|
}
|
|
|
|
grid_sizer->Add(text, 1, wxALL | wxEXPAND, border);
|
|
};
|
|
add_value(old_value, old_new_diff_set);
|
|
add_value(new_value, new_old_diff_set, true);
|
|
|
|
sizer->Add(grid_sizer, 1, wxEXPAND);
|
|
|
|
auto dlg_btns = new DialogButtons(this, {"OK"});
|
|
|
|
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
topSizer->Add(sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
|
topSizer->Add(dlg_btns , 0, wxEXPAND);
|
|
|
|
SetSizer(topSizer);
|
|
topSizer->SetSizeHints(this);
|
|
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
|
|
static PresetCollection* get_preset_collection(Preset::Type type, PresetBundle* preset_bundle = nullptr) {
|
|
if (!preset_bundle)
|
|
preset_bundle = wxGetApp().preset_bundle;
|
|
return type == Preset::Type::TYPE_PRINTER ? &preset_bundle->printers :
|
|
type == Preset::Type::TYPE_FILAMENT ? &preset_bundle->filaments :
|
|
type == Preset::Type::TYPE_SLA_MATERIAL ? &preset_bundle->sla_materials :
|
|
type == Preset::Type::TYPE_PRINT ? &preset_bundle->prints :
|
|
type == Preset::Type::TYPE_SLA_PRINT ? &preset_bundle->sla_prints :
|
|
nullptr;
|
|
}
|
|
|
|
//------------------------------------------
|
|
// DiffPresetDialog
|
|
//------------------------------------------
|
|
static std::string get_selection(PresetComboBox* preset_combo)
|
|
{
|
|
return into_u8(preset_combo->GetString(preset_combo->GetSelection()));
|
|
}
|
|
|
|
void DiffPresetDialog::create_presets_sizer()
|
|
{
|
|
m_presets_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
for (auto new_type : { Preset::TYPE_PRINTER, Preset::TYPE_FILAMENT, Preset::TYPE_SLA_MATERIAL, Preset::TYPE_PRINT, Preset::TYPE_SLA_PRINT })
|
|
{
|
|
const PresetCollection* collection = get_preset_collection(new_type);
|
|
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
PresetComboBox* presets_left;
|
|
PresetComboBox* presets_right;
|
|
ScalableButton* equal_bmp = new ScalableButton(this, wxID_ANY, "equal");
|
|
|
|
auto add_preset_combobox = [collection, sizer, new_type, this](PresetComboBox** cb_, PresetBundle* preset_bundle) {
|
|
*cb_ = new PresetComboBox(this, new_type, wxSize(em_unit() * 35, -1), preset_bundle);
|
|
PresetComboBox* cb = (*cb_);
|
|
cb->set_selection_changed_function([this, new_type, preset_bundle, cb](int selection) {
|
|
if (m_view_type == Preset::TYPE_INVALID) {
|
|
std::string preset_name = Preset::remove_suffix_modified(cb->GetString(selection).ToUTF8().data());
|
|
update_compatibility(preset_name, new_type, preset_bundle);
|
|
}
|
|
update_tree();
|
|
});
|
|
if (collection->get_selected_idx() != (size_t)-1)
|
|
cb->update(collection->get_selected_preset().name);
|
|
|
|
sizer->Add(cb, 1);
|
|
cb->Show(new_type == Preset::TYPE_PRINTER);
|
|
};
|
|
add_preset_combobox(&presets_left, m_preset_bundle_left.get());
|
|
sizer->Add(equal_bmp, 0, wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 5);
|
|
add_preset_combobox(&presets_right, m_preset_bundle_right.get());
|
|
m_presets_sizer->Add(sizer, 1, wxTOP, 5);
|
|
equal_bmp->Show(new_type == Preset::TYPE_PRINTER);
|
|
|
|
m_preset_combos.push_back({ presets_left, equal_bmp, presets_right });
|
|
|
|
equal_bmp->Bind(wxEVT_BUTTON, [presets_left, presets_right, this](wxEvent&) {
|
|
std::string preset_name = get_selection(presets_left);
|
|
presets_right->update(preset_name);
|
|
if (m_view_type == Preset::TYPE_INVALID)
|
|
update_compatibility(Preset::remove_suffix_modified(preset_name), presets_right->get_type(), m_preset_bundle_right.get());
|
|
update_tree();
|
|
});
|
|
}
|
|
}
|
|
|
|
void DiffPresetDialog::create_show_all_presets_chb()
|
|
{
|
|
m_show_all_presets = new wxCheckBox(this, wxID_ANY, _L("Show all presets (including incompatible)"));
|
|
m_show_all_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
|
|
bool show_all = m_show_all_presets->GetValue();
|
|
for (auto preset_combos : m_preset_combos) {
|
|
if (preset_combos.presets_left->get_type() == Preset::TYPE_PRINTER)
|
|
continue;
|
|
preset_combos.presets_left->show_all(show_all);
|
|
preset_combos.presets_right->show_all(show_all);
|
|
}
|
|
if (m_view_type == Preset::TYPE_INVALID)
|
|
update_tree();
|
|
});
|
|
}
|
|
|
|
void DiffPresetDialog::create_info_lines()
|
|
{
|
|
const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
|
|
|
|
m_top_info_line = new wxStaticText(this, wxID_ANY, _L("Select presets to compare"));
|
|
m_top_info_line->SetFont(font);
|
|
|
|
m_bottom_info_line = new wxStaticText(this, wxID_ANY, "");
|
|
m_bottom_info_line->SetFont(font);
|
|
}
|
|
|
|
void DiffPresetDialog::create_tree()
|
|
{
|
|
m_tree = new DiffViewCtrl(this, wxSize(em_unit() * 65, em_unit() * 40));
|
|
m_tree->AppendToggleColumn_(L"\u2714", DiffModel::colToggle, wxLinux ? 9 : 6);
|
|
m_tree->AppendBmpTextColumn("", DiffModel::colIconText, 35);
|
|
m_tree->AppendBmpTextColumn(_L("Left Preset Value"), DiffModel::colOldValue, 15);
|
|
m_tree->AppendBmpTextColumn(_L("Right Preset Value"),DiffModel::colNewValue, 15);
|
|
m_tree->Hide();
|
|
m_tree->GetColumn(DiffModel::colToggle)->SetHidden(true);
|
|
}
|
|
|
|
std::array<Preset::Type, 3> DiffPresetDialog::types_list() const
|
|
{
|
|
return PresetBundle::types_list(m_pr_technology);
|
|
}
|
|
|
|
void DiffPresetDialog::create_buttons()
|
|
{
|
|
m_buttons = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
auto show_in_bottom_info = [this](const wxString& ext_line, wxEvent* e = nullptr) {
|
|
m_bottom_info_line->SetLabel(ext_line);
|
|
m_bottom_info_line->Show(true);
|
|
Layout();
|
|
if (e) e->Skip();
|
|
};
|
|
|
|
auto dlg_btns = new DialogButtons(this, {"OK", "Cancel"});
|
|
|
|
// Transfer
|
|
auto transfer_btn = dlg_btns->GetOK();
|
|
transfer_btn->SetLabel(L("Transfer"));
|
|
transfer_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { button_event(Action::Transfer);});
|
|
|
|
|
|
auto enable_transfer = [this](const Preset::Type& type) {
|
|
const Preset& main_edited_preset = get_preset_collection(type, wxGetApp().preset_bundle)->get_edited_preset();
|
|
if (main_edited_preset.is_dirty)
|
|
return main_edited_preset.name == get_right_preset_name(type);
|
|
return true;
|
|
};
|
|
transfer_btn->Bind(wxEVT_UPDATE_UI, [this, enable_transfer, show_in_bottom_info, transfer_btn](wxUpdateUIEvent& evt) {
|
|
bool enable = m_tree->has_selection();
|
|
if (enable) {
|
|
if (m_view_type == Preset::TYPE_INVALID) {
|
|
for (const Preset::Type& type : types_list())
|
|
if (!enable_transfer(type)) {
|
|
enable = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
enable = enable_transfer(m_view_type);
|
|
|
|
if (!enable && transfer_btn->IsShown()) {
|
|
show_in_bottom_info(_L("You can only transfer to current active profile because it has been modified."));
|
|
}
|
|
}
|
|
evt.Enable(enable);
|
|
});
|
|
transfer_btn->Bind(wxEVT_ENTER_WINDOW, [show_in_bottom_info](wxMouseEvent& e) {
|
|
show_in_bottom_info(_L("Transfer the selected options from left preset to the right.\n"
|
|
"Note: New modified presets will be selected in settings tabs after close this dialog."), &e); });
|
|
|
|
// Cancel
|
|
auto cancel_btn = dlg_btns->GetCANCEL();
|
|
cancel_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { button_event(Action::Discard);});
|
|
|
|
for (Button* btn : { transfer_btn, cancel_btn }) {
|
|
btn->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { update_bottom_info(); Layout(); e.Skip(); });
|
|
}
|
|
|
|
m_buttons->Add(dlg_btns, 1, wxEXPAND);
|
|
|
|
m_buttons->Show(false);
|
|
}
|
|
|
|
void DiffPresetDialog::create_edit_sizer()
|
|
{
|
|
// Add check box for the edit mode
|
|
m_use_for_transfer = new wxCheckBox(this, wxID_ANY, _L("Transfer values from left to right"));
|
|
m_use_for_transfer->SetToolTip(_L("If enabled, this dialog can be used for transfer selected values from left to right preset."));
|
|
m_use_for_transfer->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
|
|
bool use = m_use_for_transfer->GetValue();
|
|
m_tree->GetColumn(DiffModel::colToggle)->SetHidden(!use);
|
|
if (m_tree->IsShown()) {
|
|
m_buttons->Show(use);
|
|
Fit();
|
|
Refresh();
|
|
}
|
|
else
|
|
this->Layout();
|
|
});
|
|
|
|
// Add Buttons
|
|
create_buttons();
|
|
|
|
// Create and fill edit sizer
|
|
m_edit_sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
m_edit_sizer->Add(m_use_for_transfer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
|
|
m_edit_sizer->AddSpacer(em_unit() * 10);
|
|
m_edit_sizer->Add(m_buttons, 1, wxLEFT, 5);
|
|
m_edit_sizer->Show(false);
|
|
}
|
|
|
|
void DiffPresetDialog::complete_dialog_creation()
|
|
{
|
|
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
int border = 10;
|
|
topSizer->Add(m_top_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2 * border);
|
|
topSizer->Add(m_presets_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
|
topSizer->Add(m_show_all_presets, 0, wxEXPAND | wxALL, border);
|
|
topSizer->Add(m_tree, 1, wxEXPAND | wxALL, border);
|
|
topSizer->Add(m_bottom_info_line, 0, wxEXPAND | wxALL, 2 * border);
|
|
topSizer->Add(m_edit_sizer, 0, wxEXPAND);
|
|
|
|
this->SetMinSize(wxSize(80 * em_unit(), 30 * em_unit()));
|
|
this->SetSizer(topSizer);
|
|
topSizer->SetSizeHints(this);
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
}
|
|
|
|
DiffPresetDialog::DiffPresetDialog(MainFrame* mainframe)
|
|
: DPIDialog(mainframe, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
|
m_pr_technology(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology())
|
|
{
|
|
#if defined(__WXMSW__)
|
|
// 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 // __WXMSW__
|
|
|
|
// Init bundles
|
|
|
|
assert(wxGetApp().preset_bundle);
|
|
|
|
m_preset_bundle_left = std::make_unique<PresetBundle>(*wxGetApp().preset_bundle);
|
|
m_preset_bundle_right = std::make_unique<PresetBundle>(*wxGetApp().preset_bundle);
|
|
|
|
// Create UI items
|
|
|
|
SetBackgroundColour(*wxWHITE);
|
|
|
|
create_info_lines();
|
|
|
|
create_presets_sizer();
|
|
|
|
create_show_all_presets_chb();
|
|
|
|
create_tree();
|
|
|
|
create_edit_sizer();
|
|
|
|
complete_dialog_creation();
|
|
}
|
|
|
|
void DiffPresetDialog::update_controls_visibility(Preset::Type type /* = Preset::TYPE_INVALID*/)
|
|
{
|
|
for (auto preset_combos : m_preset_combos) {
|
|
Preset::Type cb_type = preset_combos.presets_left->get_type();
|
|
bool show = type != Preset::TYPE_INVALID ? type == cb_type :
|
|
cb_type == Preset::TYPE_PRINTER ? true :
|
|
m_pr_technology == ptFFF ? cb_type == Preset::TYPE_PRINT || cb_type == Preset::TYPE_FILAMENT :
|
|
cb_type == Preset::TYPE_SLA_PRINT || cb_type == Preset::TYPE_SLA_MATERIAL;
|
|
preset_combos.presets_left->Show(show);
|
|
preset_combos.equal_bmp->Show(show);
|
|
preset_combos.presets_right->Show(show);
|
|
|
|
if (show) {
|
|
preset_combos.presets_left->update_from_bundle();
|
|
preset_combos.presets_right->update_from_bundle();
|
|
}
|
|
}
|
|
|
|
m_show_all_presets->Show(type != Preset::TYPE_PRINTER);
|
|
}
|
|
|
|
void DiffPresetDialog::update_bundles_from_app()
|
|
{
|
|
*m_preset_bundle_left = *wxGetApp().preset_bundle;
|
|
*m_preset_bundle_right = *wxGetApp().preset_bundle;
|
|
|
|
m_pr_technology = m_preset_bundle_left.get()->printers.get_edited_preset().printer_technology();
|
|
}
|
|
|
|
void DiffPresetDialog::show(Preset::Type type /* = Preset::TYPE_INVALID*/)
|
|
{
|
|
this->SetTitle(_L("Compare presets"));
|
|
m_view_type = type;
|
|
|
|
update_bundles_from_app();
|
|
update_controls_visibility(type);
|
|
if (type == Preset::TYPE_INVALID)
|
|
Fit();
|
|
|
|
update_tree();
|
|
wxGetApp().UpdateDlgDarkUI(this);
|
|
|
|
// if this dialog is shown it have to be Hide and show again to be placed on the very Top of windows
|
|
if (IsShown())
|
|
Hide();
|
|
Show();
|
|
}
|
|
|
|
void DiffPresetDialog::update_presets(Preset::Type type)
|
|
{
|
|
update_bundles_from_app();
|
|
update_controls_visibility(type);
|
|
|
|
if (type == Preset::TYPE_INVALID)
|
|
for (auto preset_combos : m_preset_combos) {
|
|
if (preset_combos.presets_left->get_type() == Preset::TYPE_PRINTER) {
|
|
preset_combos.presets_left->update_from_bundle ();
|
|
preset_combos.presets_right->update_from_bundle();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
for (auto preset_combos : m_preset_combos) {
|
|
if (preset_combos.presets_left->get_type() == type) {
|
|
preset_combos.presets_left->update();
|
|
preset_combos.presets_right->update();
|
|
break;
|
|
}
|
|
}
|
|
|
|
update_tree();
|
|
}
|
|
|
|
void DiffPresetDialog::update_bottom_info(wxString bottom_info)
|
|
{
|
|
const bool show_bottom_info = !m_tree->IsShown();
|
|
if (show_bottom_info)
|
|
m_bottom_info_line->SetLabel(bottom_info);
|
|
m_bottom_info_line->Show(show_bottom_info);
|
|
}
|
|
|
|
void DiffPresetDialog::update_tree()
|
|
{
|
|
Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
|
|
searcher.sort_options_by_key();
|
|
|
|
m_tree->Clear();
|
|
wxString bottom_info = "";
|
|
bool show_tree = false;
|
|
|
|
for (auto preset_combos : m_preset_combos)
|
|
{
|
|
if (!preset_combos.presets_left->IsShown())
|
|
continue;
|
|
Preset::Type type = preset_combos.presets_left->get_type();
|
|
|
|
const PresetCollection* presets = get_preset_collection(type);
|
|
const Preset* left_preset = presets->find_preset(get_selection(preset_combos.presets_left));
|
|
const Preset* right_preset = presets->find_preset(get_selection(preset_combos.presets_right));
|
|
if (!left_preset || !right_preset) {
|
|
bottom_info = _L("One of the presets does not exist");
|
|
preset_combos.equal_bmp->SetBitmap_(ScalableBitmap(this, "question"));
|
|
preset_combos.equal_bmp->SetToolTip(bottom_info);
|
|
continue;
|
|
}
|
|
|
|
const DynamicPrintConfig& left_config = left_preset->config;
|
|
const PrinterTechnology& left_pt = left_preset->printer_technology();
|
|
const DynamicPrintConfig& right_congig = right_preset->config;
|
|
|
|
if (left_pt != right_preset->printer_technology()) {
|
|
bottom_info = _L("Compared presets has different printer technology");
|
|
preset_combos.equal_bmp->SetBitmap_(ScalableBitmap(this, "question"));
|
|
preset_combos.equal_bmp->SetToolTip(bottom_info);
|
|
continue;
|
|
}
|
|
|
|
// Collect dirty options.
|
|
const bool deep_compare = (type == Preset::TYPE_PRINTER ||
|
|
type == Preset::TYPE_FILAMENT || type == Preset::TYPE_SLA_MATERIAL);
|
|
auto dirty_options = type == Preset::TYPE_PRINTER && left_pt == ptFFF &&
|
|
left_config.opt<ConfigOptionStrings>("extruder_colour")->values.size() < right_congig.opt<ConfigOptionStrings>("extruder_colour")->values.size() ?
|
|
presets->dirty_options(right_preset, left_preset, deep_compare) :
|
|
presets->dirty_options(left_preset, right_preset, deep_compare);
|
|
|
|
if (dirty_options.empty()) {
|
|
//bottom_info = _L("Presets are the same");
|
|
bottom_info = wxEmptyString;
|
|
preset_combos.equal_bmp->SetBitmap_(ScalableBitmap(this, "equal"));
|
|
preset_combos.equal_bmp->SetToolTip(bottom_info);
|
|
continue;
|
|
}
|
|
|
|
show_tree = true;
|
|
preset_combos.equal_bmp->SetBitmap_(ScalableBitmap(this, "not_equal"));
|
|
/*preset_combos.equal_bmp->SetToolTip(_L("Presets are different.\n"
|
|
"Click this button to select the same preset for the right and left preset."));*/
|
|
|
|
preset_combos.equal_bmp->SetToolTip(wxEmptyString);
|
|
|
|
m_tree->model->AddPreset(type, "\"" + from_u8(left_preset->name) + "\" vs \"" + from_u8(right_preset->name) + "\"", left_pt);
|
|
|
|
const std::map<wxString, std::string>& category_icon_map = wxGetApp().get_tab(type)->get_category_icon_map();
|
|
auto get_category_icon = [&category_icon_map](const wxString& key) {
|
|
auto it = category_icon_map.find(key);
|
|
return it != category_icon_map.end() ? it->second : std::string();
|
|
};
|
|
|
|
// process changes of extruders count
|
|
if (type == Preset::TYPE_PRINTER && left_pt == ptFFF &&
|
|
left_config.opt<ConfigOptionStrings>("extruder_colour")->values.size() != right_congig.opt<ConfigOptionStrings>("extruder_colour")->values.size()) {
|
|
wxString local_label = _L("Extruder count");
|
|
wxString left_val = from_u8((boost::format("%1%") % left_config.opt<ConfigOptionStrings>("extruder_colour")->values.size()).str());
|
|
wxString right_val = from_u8((boost::format("%1%") % right_congig.opt<ConfigOptionStrings>("extruder_colour")->values.size()).str());
|
|
|
|
m_tree->Append("extruders_count", type, _L("General"), _L("Capabilities"), local_label, left_val, right_val,
|
|
get_category_icon("Basic information"));
|
|
}
|
|
|
|
for (const std::string& opt_key : dirty_options) {
|
|
wxString left_val = get_string_value(opt_key, left_config);
|
|
wxString right_val = get_string_value(opt_key, right_congig);
|
|
|
|
const std::string lookup_key = get_pure_opt_key(opt_key);
|
|
Search::Option option = searcher.get_option(lookup_key, get_full_label(lookup_key, left_config), type);
|
|
if (get_pure_opt_key(option.opt_key()) != lookup_key)
|
|
option = searcher.get_option(opt_key, get_full_label(opt_key, left_config), type);
|
|
if (get_pure_opt_key(option.opt_key()) != lookup_key) {
|
|
// When the found option is not the requested one.
|
|
// This can happen for dirty_options such as:
|
|
// "default_print_profile", "printer_model", "printer_settings_id",
|
|
// because they do not exist in the searcher.
|
|
continue;
|
|
}
|
|
m_tree->Append(opt_key, type, option.category_local, option.group_local, option.label_local,
|
|
left_val, right_val, get_category_icon(option.category));
|
|
}
|
|
}
|
|
|
|
bool tree_was_shown = m_tree->IsShown();
|
|
m_tree->Show(show_tree);
|
|
|
|
bool can_transfer_options = m_view_type == Preset::TYPE_INVALID || get_left_preset_name(m_view_type) != get_right_preset_name(m_view_type);
|
|
m_edit_sizer->Show(show_tree && can_transfer_options);
|
|
m_buttons->Show(m_edit_sizer->IsShown(size_t(0)) && m_use_for_transfer->GetValue());
|
|
|
|
update_bottom_info(bottom_info);
|
|
|
|
if (tree_was_shown == m_tree->IsShown())
|
|
Layout();
|
|
else {
|
|
Fit();
|
|
Refresh();
|
|
}
|
|
|
|
// Revert sort of searcher back
|
|
searcher.sort_options_by_label();
|
|
}
|
|
|
|
void DiffPresetDialog::on_dpi_changed(const wxRect&)
|
|
{
|
|
int em = em_unit();
|
|
|
|
const wxSize& size = wxSize(80 * em, 30 * em);
|
|
SetMinSize(size);
|
|
|
|
for (auto preset_combos : m_preset_combos) {
|
|
preset_combos.presets_left->msw_rescale();
|
|
preset_combos.equal_bmp->msw_rescale();
|
|
preset_combos.presets_right->msw_rescale();
|
|
}
|
|
|
|
m_tree->Rescale(em);
|
|
|
|
Fit();
|
|
Refresh();
|
|
}
|
|
|
|
void DiffPresetDialog::on_sys_color_changed()
|
|
{
|
|
#ifdef _WIN32
|
|
wxGetApp().UpdateAllStaticTextDarkUI(this);
|
|
wxGetApp().UpdateDarkUI(m_show_all_presets);
|
|
wxGetApp().UpdateDVCDarkUI(m_tree);
|
|
#endif
|
|
|
|
for (auto preset_combos : m_preset_combos) {
|
|
preset_combos.presets_left->msw_rescale();
|
|
preset_combos.equal_bmp->msw_rescale();
|
|
preset_combos.presets_right->msw_rescale();
|
|
}
|
|
|
|
// msw_rescale updates just icons, so use it
|
|
m_tree->Rescale();
|
|
Refresh();
|
|
}
|
|
|
|
void DiffPresetDialog::update_compatibility(const std::string& preset_name, Preset::Type type, PresetBundle* preset_bundle)
|
|
{
|
|
PresetCollection* presets = get_preset_collection(type, preset_bundle);
|
|
|
|
bool print_tab = type == Preset::TYPE_PRINT || type == Preset::TYPE_SLA_PRINT;
|
|
bool printer_tab = type == Preset::TYPE_PRINTER;
|
|
bool technology_changed = false;
|
|
|
|
if (printer_tab) {
|
|
const Preset& new_printer_preset = *presets->find_preset(preset_name, true);
|
|
PrinterTechnology old_printer_technology = presets->get_selected_preset().printer_technology();
|
|
PrinterTechnology new_printer_technology = new_printer_preset.printer_technology();
|
|
|
|
technology_changed = old_printer_technology != new_printer_technology;
|
|
}
|
|
|
|
// select preset
|
|
presets->select_preset_by_name(preset_name, false);
|
|
|
|
// Mark the print & filament enabled if they are compatible with the currently selected preset.
|
|
// The following method should not discard changes of current print or filament presets on change of a printer profile,
|
|
// if they are compatible with the current printer.
|
|
auto update_compatible_type = [](bool technology_changed, bool on_page, bool show_incompatible_presets) {
|
|
return technology_changed ? PresetSelectCompatibleType::Always :
|
|
on_page ? PresetSelectCompatibleType::Never :
|
|
show_incompatible_presets ? PresetSelectCompatibleType::OnlyIfWasCompatible : PresetSelectCompatibleType::Always;
|
|
};
|
|
if (print_tab || printer_tab)
|
|
preset_bundle->update_compatible(
|
|
update_compatible_type(technology_changed, print_tab, true),
|
|
update_compatible_type(technology_changed, false, true));
|
|
|
|
bool is_left_presets = preset_bundle == m_preset_bundle_left.get();
|
|
PrinterTechnology pr_tech = preset_bundle->printers.get_selected_preset().printer_technology();
|
|
|
|
// update preset comboboxes
|
|
for (auto preset_combos : m_preset_combos)
|
|
{
|
|
PresetComboBox* cb = is_left_presets ? preset_combos.presets_left : preset_combos.presets_right;
|
|
Preset::Type presets_type = cb->get_type();
|
|
if ((print_tab && (
|
|
(pr_tech == ptFFF && presets_type == Preset::TYPE_FILAMENT) ||
|
|
(pr_tech == ptSLA && presets_type == Preset::TYPE_SLA_MATERIAL) )) ||
|
|
(printer_tab && (
|
|
(pr_tech == ptFFF && (presets_type == Preset::TYPE_PRINT || presets_type == Preset::TYPE_FILAMENT) ) ||
|
|
(pr_tech == ptSLA && (presets_type == Preset::TYPE_SLA_PRINT || presets_type == Preset::TYPE_SLA_MATERIAL) )) ))
|
|
cb->update();
|
|
}
|
|
|
|
if (technology_changed &&
|
|
m_preset_bundle_left.get()->printers.get_selected_preset().printer_technology() ==
|
|
m_preset_bundle_right.get()->printers.get_selected_preset().printer_technology())
|
|
{
|
|
m_pr_technology = m_preset_bundle_left.get()->printers.get_edited_preset().printer_technology();
|
|
update_controls_visibility();
|
|
}
|
|
}
|
|
|
|
void DiffPresetDialog::button_event(Action act)
|
|
{
|
|
Hide();
|
|
if (act == Action::Transfer)
|
|
wxPostEvent(this, SimpleEvent(EVT_DIFF_DIALOG_TRANSFER));
|
|
}
|
|
|
|
std::string DiffPresetDialog::get_left_preset_name(Preset::Type type)
|
|
{
|
|
PresetComboBox* cb = std::find_if(m_preset_combos.begin(), m_preset_combos.end(), [type](const DiffPresets& p) {
|
|
return p.presets_left->get_type() == type;
|
|
})->presets_left;
|
|
return Preset::remove_suffix_modified(get_selection(cb));
|
|
}
|
|
|
|
std::string DiffPresetDialog::get_right_preset_name(Preset::Type type)
|
|
{
|
|
PresetComboBox* cb = std::find_if(m_preset_combos.begin(), m_preset_combos.end(), [type](const DiffPresets& p) {
|
|
return p.presets_right->get_type() == type;
|
|
})->presets_right;
|
|
return Preset::remove_suffix_modified(get_selection(cb));
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace Slic3r::GUI
|