mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-19 13:41:03 +00:00
* Add orca-translations Claude skill and PO tool
Add a .claude skill that guides translation passes over the localization/i18n gettext catalogs. It covers the workflow: run gettext, settle new glossary terms, translate in batches, verify, and report.
Includes translation-rules.md, which condenses the placeholder, line-break and glossary rules plus the guidance on review notes.
Also adds scripts/po_tool.py (standard library only). It lists pending entries with glossary hints, validates and applies translations, adding '# AI Translated' and '# Needs human review' comments, and rewrites only the entries it changes. It can also check catalogs, look up existing translations, suggest glossary candidates and update the wiki glossary TSV.
* Let po_tool apply translations by entry id
pending now gives each entry a short id, so a translation file can name
entries by id instead of repeating the English msgid. apply accepts either.
A "review": "" item clears an earlier review note when overwriting an
entry, and check gives each msgfmt run its own temporary output file so
parallel checks do not collide.
* Fill all empty translations
Regenerate the template with run_gettext --full and translate every empty
entry in the 22 catalogs (7,089 entries) with the orca-translations skill,
following the localization glossary, including the new belt printer,
IDEX/IQEX and plugin terms. Every new entry is marked "# AI Translated";
122 also carry "# Needs human review" with what to check.
* Show entry ids in po_tool lookup and search translations
lookup now prints each entry's id, so an entry found there can be
rewritten with apply --overwrite without repeating the English, and
--msgstr searches the translations instead of the msgids, which finds
every entry that still uses a term being changed.
SKILL.md describes how to change an established term: check the
catalog's usage first, update the glossary cell, then rewrite only the
entries where the term has that meaning.
* Fix mistranslated terms and align catalogs with the glossary
Correct labels that the glossary had copied, and the entries that used
them:
- ca: Raft was rendered as the Brim term; it is now "Rai". Exhaust fan,
Monotonic line, Unretract, Slice, Lateral Honeycomb and PA Tower
labels fixed.
- cs: Lightning infill is "Blesk", not "Osvětlení" (illumination).
- sv: Retraction is "Retraktion", not "Reduktion"; Unretract,
Lagerhöjd, Delkylningsfläkt, Utsugsfläkt, Aligned Rectilinear and the
Preset label fixed.
- de: Brim is "Rand" and Skirt is "Umrandung", as most entries already
had them.
- lt: brim entries used the Skirt word "apvadas"; they now use "kraštas".
- es: Sparse infill is "relleno de baja densidad", the catalog majority.
- eu: nozzle wipe is "garbiketa", not purge.
- th: the project plate is "เพลต", distinct from the bed.
- uk: Quarter Cubic, Latin "PA" in PA Tower, and Belt purge tower.
- pl: Cross Hatch, Tri-hexagon, Volumetric speed and Raft labels.
- zh_TW: Brim stays "Brim", as the existing labels have it.
- tr: "Prime Tower" read "Prime Minister Tower".
- ko: the plugin "Capabilities" label read "performance".
- Build Volume labels fixed in tr, de, cs and eu.
All 242 rewritten entries are marked "# AI Translated".
* Make more UI text translatable and fix pluralization and context
Audit the sources for text that translators could not translate
correctly and fix it.
Mark text that reached users in English only: IDEX/IQEX role names,
plugin permission prompts and errors, 3MF/preset loading errors, G-code
placeholder parser errors, calibration and device messages, notification
texts, file dialog filters, and the config validation messages.
Replace sentences built from translated fragments, such as the prime
tower warnings, "left"/"right" nozzle names, and "Filament " + number,
with single msgids that have placeholders, so translators can reorder
and inflect them.
Use plural forms for texts with a count ("%1% mixed filaments",
"%d objects", "%1% triangles") instead of "(s)" and mismatched verbs.
Fix the swapped singular/plural pair in "The following presets inherit
this preset".
Add translation contexts where one English word has several meanings
(Subscribed/Local, Standard, Pause, View, Step, Model, Camera, Flow,
Filter, Others, Prepare, the Design tools). Show "Size" instead of
"Build Volume" in the Cut tool, which displays the object size.
Fix code that compared translated text with English (the Edit G-code
dialog group, the plugin registration failure check), and add the
bundled web pages' missing string-table entries. Use the single
character for degrees Celsius, say "G-code", and describe the head
filament map without internal jargon.
Install the libslic3r translate callback before the presets load, so
their load errors are translated, and list the 28 source files that had
marked strings but were never extracted into localization/i18n/list.txt.
* Suggest earlier translations and list fuzzy entries in po_tool pending
pending now attaches the closest earlier translation of a similar
English text (from obsolete or translated entries), so a reworded or
split string keeps its wording, and it lists fuzzy entries by default,
since users never see them. A fuzzy entry also carries its stale
translation as previous_translation.
* Fix wording of the non-boolean operands error
The logical operation error said "operators" where it means the
operands the operation was applied to.
* Translate the newly translatable strings in all languages
Regenerate the template after the translatability changes and fill every
new or changed entry in the 22 catalogs (about 12,800 entries): the
strings that became translatable, the plural forms, the entries split by
context, and the previously fuzzy singular entries that became plurals.
Every new entry is marked "# AI Translated", and about 360 also carry a
"# Needs human review" note saying what to check. Terms follow the
localization glossary. Earlier translations of reworded strings were
reused where the meaning was unchanged. Existing entries were not
changed, and obsolete entries are kept.
* Document the source translatability audit in the orca-translations skill
Add a reference describing how strings reach translators, what to look
for (untranslated text, spliced sentences, missing plurals, ambiguous
words), what not to translate, where a context cannot be carried, and
what to do after the edits, plus a scanner that lists untranslated
user-facing string literals.
* Document how to write translatable strings in AGENTS.md
Tell developers to keep each message complete in one msgid: use
placeholders instead of concatenating translated fragments, plural forms
for counts, and contexts for ambiguous words, and to list new source
files in localization/i18n/list.txt. Add a matching code review item.
* Translate the strings added since the rebase
Regenerate the template against the current main and translate the 26
new entries per language: the Fix Model commands and messages, and the
texture displacement image adjustment panel. Each new entry is marked
"# AI Translated"; existing entries are unchanged.
* tidy
2351 lines
92 KiB
C++
2351 lines
92 KiB
C++
#include "UnsavedChangesDialog.hpp"
|
|
|
|
#include <cmath>
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
#include <cassert>
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include "slic3r/GUI/Event.hpp"
|
|
#include <map>
|
|
#include "libslic3r/Preset.hpp"
|
|
#include "libslic3r/Config.hpp"
|
|
#include "slic3r/GUI/GUI.hpp"
|
|
#include "slic3r/GUI/I18N.hpp"
|
|
#include "slic3r/GUI/Gizmos/GLGizmoBase.hpp"
|
|
#include <memory>
|
|
#include "slic3r/GUI/GUI_Utils.hpp"
|
|
#include "slic3r/GUI/Widgets/Label.hpp"
|
|
#include "slic3r/GUI/DeviceCore/DevConfigUtil.h"
|
|
#include "slic3r/GUI/DeviceCore/DevDefs.h"
|
|
#include "slic3r/GUI/Widgets/StateColor.hpp"
|
|
#include "slic3r/GUI/Widgets/Button.hpp"
|
|
#include "slic3r/GUI/SettingsIndex.hpp"
|
|
#include <set>
|
|
#include <iterator>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <wx/colour.h>
|
|
#include <wx/dataview.h>
|
|
#include <wx/log.h>
|
|
#include <wx/gdicmn.h>
|
|
#include <wx/dvrenderers.h>
|
|
#include <wx/panel.h>
|
|
#include <wx/string.h>
|
|
#include <wx/event.h>
|
|
#include <wx/stattext.h>
|
|
#include <wx/dialog.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/textctrl.h>
|
|
#include <wx/settings.h>
|
|
#include <wx/tokenzr.h>
|
|
#include <wx/utils.h>
|
|
#include <wx/variant.h>
|
|
#include <wx/window.h>
|
|
#include <wx/toplevel.h>
|
|
|
|
#include "CalibUtils.hpp"
|
|
#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"
|
|
|
|
#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))
|
|
{
|
|
SetBackgroundColour(wxColour("#FFFFFF"));
|
|
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(wxColour("#363636"));
|
|
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(wxColour("#D9D9D9"));
|
|
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("#D9D9D9"));
|
|
|
|
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(wxColour("#363636"));
|
|
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, 1, 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() : wxColour("#363636"));
|
|
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() : wxColour("#363636"));
|
|
|
|
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_table_top->SetSizer(m_sizer_top);
|
|
m_table_top->Layout();
|
|
m_sizer_top->Fit(m_table_top);
|
|
m_sizer_tab->Add(m_table_top, 1, 0, 0);
|
|
|
|
m_scrolledWindow = new wxScrolledWindow(m_panel_tab, wxID_ANY, wxDefaultPosition, UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE, wxNO_BORDER|wxVSCROLL);
|
|
m_scrolledWindow->SetScrollRate(0, FromDIP(20));
|
|
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_btn = new Button(this, "", "toolbar_wiki", 0, 15);
|
|
auto wiki_url = "https://www.orcaslicer.com/wiki/transfer_discard_changes";
|
|
wiki_btn->SetToolTip(_L("Wiki Guide") + "\n" + wiki_url);
|
|
wiki_btn->SetStyle(ButtonStyle::Confirm, ButtonType::Circle);
|
|
wiki_btn->SetCanFocus(false);
|
|
wiki_btn->Bind(wxEVT_LEFT_DOWN, ([wiki_url](auto& e) {wxLaunchDefaultBrowser(wiki_url);}));
|
|
|
|
m_sizer_button->Add(wiki_btn, 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::SettingsIndex &index = wxGetApp().sidebar().settings_index();
|
|
index.sort_options_by_key();
|
|
|
|
for (const std::string &opt_key : config->keys()) {
|
|
int variant_index = -2;
|
|
Search::Option option = index.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::SettingsIndex& index = wxGetApp().sidebar().settings_index();
|
|
index.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 = index.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 the index
|
|
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 index back
|
|
index.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);
|
|
|
|
// show() copies the app's bundle into both before anything is displayed.
|
|
m_preset_bundle_left = std::make_unique<PresetBundle>();
|
|
m_preset_bundle_right = std::make_unique<PresetBundle>();
|
|
|
|
// 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::SettingsIndex& index = wxGetApp().sidebar().settings_index();
|
|
index.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);
|
|
|
|
// TRN Header of a preset comparison: the two preset names.
|
|
m_tree->model->AddPreset(type, format_wxstr(_L("\"%1%\" vs \"%2%\""), left_preset->name, 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 = index.get_option(lookup_key, get_full_label(lookup_key, left_config), type);
|
|
if (get_pure_opt_key(option.opt_key()) != lookup_key)
|
|
option = index.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 index.
|
|
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 index back
|
|
index.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
|