PrusaSlicer/src/slic3r/GUI/UpdatesUIManager.cpp
2024-06-20 16:05:21 +02:00

356 lines
11 KiB
C++

///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "UpdatesUIManager.hpp"
#include "I18N.hpp"
#include "wxExtensions.hpp"
#include "PresetArchiveDatabase.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
#include "MsgDialog.hpp"
#include "format.hpp"
#include "Widgets/CheckBox.hpp"
#include <wx/wupdlock.h>
namespace fs = boost::filesystem;
namespace Slic3r {
namespace GUI {
RepositoryUpdateUIManager::RepositoryUpdateUIManager(wxWindow* parent, PresetArchiveDatabase* pad, int em) :
m_parent(parent)
,m_pad(pad)
,m_main_sizer(new wxBoxSizer(wxVERTICAL))
{
auto online_label = new wxStaticText(m_parent, wxID_ANY, _L("Online sources"));
online_label->SetFont(wxGetApp().bold_font());
m_main_sizer->Add(online_label, 0, wxTOP | wxLEFT, 2 * em);
m_online_sizer = new wxFlexGridSizer(4, 0.75 * em, 1.5 * em);
m_online_sizer->AddGrowableCol(2);
m_online_sizer->AddGrowableCol(3);
m_online_sizer->SetFlexibleDirection(/*wxHORIZONTAL*/wxBOTH);
m_main_sizer->Add(m_online_sizer, 0, wxALL, 2 * em);
m_main_sizer->AddSpacer(em);
auto offline_label = new wxStaticText(m_parent, wxID_ANY, _L("Local sources"));
offline_label->SetFont(wxGetApp().bold_font());
m_main_sizer->Add(offline_label, 0, wxTOP | wxLEFT, 2 * em);
m_offline_sizer = new wxFlexGridSizer(7, 0.75 * em, 1.5 * em);
m_offline_sizer->AddGrowableCol(1);
m_offline_sizer->AddGrowableCol(2);
m_offline_sizer->AddGrowableCol(4);
m_offline_sizer->SetFlexibleDirection(wxHORIZONTAL);
m_main_sizer->Add(m_offline_sizer, 0, wxALL, 2 * em);
fill_entries(true);
fill_grids();
}
void RepositoryUpdateUIManager::fill_entries(bool init_selection/* = false*/)
{
m_online_entries.clear();
m_offline_entries.clear();
const SharedArchiveRepositoryVector& archs = m_pad->get_all_archive_repositories();
for (const auto* archive : archs) {
const std::string& uuid = archive->get_uuid();
if (init_selection && m_pad->is_selected_repository_by_uuid(uuid))
m_selected_uuids.emplace(uuid);
const bool is_selected = m_selected_uuids.find(uuid) != m_selected_uuids.end();
const auto& data = archive->get_manifest();
if (data.source_path.empty()) {
// online repo
m_online_entries.push_back({ is_selected, uuid, data.name, data.description, data.visibility });
}
else {
// offline repo
m_offline_entries.push_back({ is_selected, uuid, data.name, data.description, data.source_path.filename().string(), fs::exists(data.source_path), data.source_path });
}
}
}
void RepositoryUpdateUIManager::fill_grids()
{
// clear grids
m_online_sizer->Clear(true);
m_offline_sizer->Clear(true);
// Fill Online repository
if (!m_online_entries.empty()) {
auto add = [this](wxWindow* win) { m_online_sizer->Add(win, 0, wxALIGN_CENTER_VERTICAL); };
// header
// TRN: This string appears in Configuration Wizard in the 'Configuration Manager' step.
for (const wxString& l : std::initializer_list<wxString>{ "", "", _L("Name"), _L("Description")}) {
auto text = new wxStaticText(m_parent, wxID_ANY, l);
text->SetFont(wxGetApp().bold_font());
add(text);
}
// data
for (const auto& entry : m_online_entries)
{
auto chb = CheckBox::GetNewWin(m_parent, "");
CheckBox::SetValue(chb, entry.use);
chb->Bind(wxEVT_CHECKBOX, [this, chb, &entry](wxCommandEvent e) {
if (CheckBox::GetValue(chb))
m_selected_uuids.emplace(entry.id);
else
m_selected_uuids.erase(entry.id);
check_selection();
});
add(chb);
if (entry.visibility.empty())
add(new wxStaticText(m_parent, wxID_ANY, ""));
else {
wxStaticBitmap* bmp = new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle("info"));
bmp->SetToolTip(from_u8(entry.visibility));
add(bmp);
}
add(new wxStaticText(m_parent, wxID_ANY, from_u8(entry.name)));
add(new wxStaticText(m_parent, wxID_ANY, from_u8(entry.description)));
}
}
if (!m_offline_entries.empty()) {
auto add = [this](wxWindow* win) { m_offline_sizer->Add(win, 0, wxALIGN_CENTER_VERTICAL); };
// header
for (const wxString& l : std::initializer_list<wxString>{ "", _L("Name"), _L("Description"), "", _L("Source file"), "", ""}) {
auto text = new wxStaticText(m_parent, wxID_ANY, l);
text->SetFont(wxGetApp().bold_font());
add(text);
}
// data1
for (const auto& entry : m_offline_entries)
{
auto chb = CheckBox::GetNewWin(m_parent, "");
CheckBox::SetValue(chb, entry.use);
chb->Bind(wxEVT_CHECKBOX, [this, chb, &entry](wxCommandEvent e) {
if (CheckBox::GetValue(chb))
m_selected_uuids.emplace(entry.id);
else
m_selected_uuids.erase(entry.id);
check_selection();
});
add(chb);
add(new wxStaticText(m_parent, wxID_ANY, from_u8(entry.name)));
add(new wxStaticText(m_parent, wxID_ANY, from_u8(entry.description)));
{
wxStaticBitmap* bmp = new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle(entry.is_ok ? "tick_mark" : "exclamation"));
bmp->SetToolTip(entry.is_ok ? _L("File exists") : _L("File does NOT exist"));
add(bmp);
}
{
auto path_str = new wxStaticText(m_parent, wxID_ANY, from_u8(entry.source));
path_str->SetToolTip(from_u8(entry.source_path.string()));
add(path_str);
}
{
ScalableButton* btn = new ScalableButton(m_parent, wxID_ANY, "open");
btn->SetToolTip(_L("Open folder"));
btn->Bind(wxEVT_BUTTON, [&entry](wxCommandEvent& event) {
GUI::desktop_open_folder(entry.source_path.parent_path().make_preferred());
});
add(btn);
}
{
wxButton* btn = new wxButton(m_parent, wxID_ANY, " " + _L("Remove") + " ");
wxGetApp().UpdateDarkUI(btn, true);
btn->Bind(wxEVT_BUTTON, [this, &entry](wxCommandEvent& event) { remove_offline_repos(entry.id); });
add(btn);
}
}
}
{
wxButton* btn = new wxButton(m_parent, wxID_ANY, " " + _L("Load") + "... ");
wxGetApp().UpdateDarkUI(btn, true);
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { load_offline_repos(); });
m_offline_sizer->Add(btn);
}
}
void RepositoryUpdateUIManager::update()
{
fill_entries();
wxWindowUpdateLocker freeze_guard(m_parent);
fill_grids();
m_parent->GetSizer()->Layout();
if (wxDialog* dlg = dynamic_cast<wxDialog*>(m_parent)) {
m_parent->Layout();
m_parent->Refresh();
dlg->Fit();
}
else if (wxWindow* top_parent = m_parent->GetParent()) {
top_parent->Layout();
top_parent->Refresh();
}
}
void RepositoryUpdateUIManager::remove_offline_repos(const std::string& id)
{
m_pad->remove_local_archive(id);
m_selected_uuids.erase(id);
check_selection();
if (wxDialog* dlg = dynamic_cast<wxDialog*>(m_parent)) {
// Invalidate min_size for correct next Layout()
dlg->SetMinSize(wxDefaultSize);
}
update();
}
void RepositoryUpdateUIManager::load_offline_repos()
{
wxArrayString input_files;
wxFileDialog dialog(m_parent, _L("Choose one or more ZIP files") + ":",
from_u8(wxGetApp().app_config->get_last_dir()), "",
file_wildcards(FT_ZIP), wxFD_OPEN | /*wxFD_MULTIPLE | */wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() == wxID_OK)
dialog.GetPaths(input_files);
if (input_files.IsEmpty())
return;
// Iterate through the input files
for (size_t i = 0; i < input_files.size(); ++i) {
std::string input_file = into_u8(input_files.Item(i));
try {
fs::path input_path = fs::path(input_file);
std::string msg;
std::string uuid = m_pad->add_local_archive(input_path, msg);
if (uuid.empty()) {
ErrorDialog(m_parent, msg, false).ShowModal();
}
else {
m_selected_uuids.emplace(uuid);
check_selection();
update();
}
}
catch (fs::filesystem_error const& e) {
std::cerr << e.what() << '\n';
}
}
}
bool RepositoryUpdateUIManager::set_selected_repositories()
{
std::vector<std::string> used_ids;
std::copy(m_selected_uuids.begin(), m_selected_uuids.end(), std::back_inserter(used_ids));
std::string msg;
if (m_pad->set_selected_repositories(used_ids, msg)) {
check_selection();
return true;
}
ErrorDialog(m_parent, msg, false).ShowModal();
// update selection on UI
update();
check_selection();
return false;
}
void RepositoryUpdateUIManager::check_selection()
{
for (const auto& [uuid, is_selected] : m_pad->get_selected_repositories_uuid() )
if ((is_selected && m_selected_uuids.find(uuid) == m_selected_uuids.end() )||
(!is_selected && m_selected_uuids.find(uuid) != m_selected_uuids.end())) {
m_is_selection_changed = true;
return;
}
m_is_selection_changed = false;
}
ManagePresetRepositoriesDialog::ManagePresetRepositoriesDialog(PresetArchiveDatabase* pad)
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY,
format_wxstr("%1% - %2%", SLIC3R_APP_NAME, _L("Manage Updates")),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
this->SetFont(wxGetApp().normal_font());
const int em = em_unit();
m_manager = std::make_unique<RepositoryUpdateUIManager>(this, pad, em);
auto sizer = m_manager->get_sizer();
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK | wxCLOSE);
wxGetApp().SetWindowVariantForButton(buttons->GetCancelButton());
wxGetApp().UpdateDlgDarkUI(this, true);
this->SetEscapeId(wxID_CLOSE);
this->Bind(wxEVT_BUTTON, &ManagePresetRepositoriesDialog::onCloseDialog, this, wxID_CLOSE);
this->Bind(wxEVT_BUTTON, &ManagePresetRepositoriesDialog::onOkDialog, this, wxID_OK);
sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, em);
buttons->GetAffirmativeButton()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& event) {
event.Enable(m_manager->has_selections());
});
SetSizer(sizer);
sizer->SetSizeHints(this);
}
void ManagePresetRepositoriesDialog::on_dpi_changed(const wxRect &suggested_rect)
{
SetMinSize(GetBestSize());
Fit();
Refresh();
}
void ManagePresetRepositoriesDialog::onCloseDialog(wxEvent &)
{
this->EndModal(wxID_CLOSE);
}
void ManagePresetRepositoriesDialog::onOkDialog(wxEvent&)
{
if (m_manager->set_selected_repositories())
this->EndModal(wxID_OK);
}
} // namespace GUI
} // namespace Slic3r