PrusaSlicer/src/slic3r/GUI/WifiConfigDialog.cpp

304 lines
12 KiB
C++

#include "WifiConfigDialog.hpp"
#include "GUI_App.hpp"
#include "GUI.hpp"
#include "I18N.hpp"
#include "format.hpp"
#include "RemovableDriveManager.hpp"
#include "MsgDialog.hpp"
#include <wx/stattext.h>
#include <wx/button.h>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/log/trivial.hpp>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "Widgets/ComboBox.hpp"
namespace Slic3r {
namespace GUI {
const char* WIFI_CONFIGFILE_NAME = "prusa_printer_settings.ini";
WifiConfigDialog::WifiConfigDialog(wxWindow* parent, std::string& file_path, RemovableDriveManager* removable_manager, const wxString& preffered_drive)
// TRN: This is the dialog title.
: DPIDialog(parent, wxID_ANY, _L("Wi-Fi Configuration File Generator"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, m_wifi_scanner(new WifiScanner())
, out_file_path(file_path)
, m_removable_manager(removable_manager)
{
wxPanel* panel = new wxPanel(this);
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
panel->SetSizer(vsizer);
// TRN Wifi config dialog explanation line 1.
wxStaticText* explain_label1 = new wxStaticText(panel, wxID_ANY, _L("Generate a file to be loaded by a Prusa printer to configure its Wi-Fi connection."));
// TRN Wifi config dialog explanation line 2.
wxStaticText* explain_label2 = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr(_L("Write this file on the USB flash drive. Its name will be %1%."), WIFI_CONFIGFILE_NAME));
// TRN Wifi config dialog explanation line 3.
wxStaticText* explain_label3 = new wxStaticText(panel, wxID_ANY, _L("Your Prusa printer should load this file automatically."));
// TRN Wifi config dialog explanation line 4.
wxStaticText* explain_label4 = new wxStaticText(panel, wxID_ANY, _L("Note: This file will contain the SSID and password in plain text."));
auto* ssid_sizer = new wxBoxSizer(wxHORIZONTAL);
// TRN SSID of WiFi network. It is a standard abbreviation which should probably not change in most languages.
wxStaticText* ssid_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("SSID")));
m_ssid_combo = new ::ComboBox(panel, wxID_ANY, wxString(""), wxDefaultPosition, wxDefaultSize, 0, nullptr, DD_NO_CHECK_ICON);
#if __APPLE__
m_ssid_combo->SetToolTip(_L("On some versions of MacOS, this only loads SSID of connected network."));
#endif // __APPLE__
rescan_networks(false);
m_ssid_button_id = NewControlId();
// TRN Text of button to rescan visible networks in Wifi Config dialog.
wxButton* ssid_button = new wxButton(panel, m_ssid_button_id, _(L("Rescan")));
ssid_sizer->Add(m_ssid_combo, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, 10);
ssid_sizer->Add(ssid_button, 0);
auto* pass_sizer = new wxBoxSizer(wxHORIZONTAL);
// TRN Password of WiFi network.
wxStaticText* password_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("Password")));
m_pass_textctrl = new ::TextInput(panel, "", "", "", wxDefaultPosition, wxDefaultSize);
#if __APPLE__
pass_sizer->Add(m_pass_textctrl, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, 10);
m_pass_button_id = NewControlId();
// TRN Text of button to retrieve password from keychain in Wifi Config dialog. Only on Mac.
wxButton* pass_button = new wxButton(panel, m_pass_button_id, _(L("Retrieve")));
pass_sizer->Add(pass_button, 0);
pass_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_retrieve_password, this);
#else
pass_sizer->Add(m_pass_textctrl, 1, wxALIGN_CENTER_VERTICAL, 10);
#endif // __APPLE__
// show password if current ssid was selected already
fill_password();
auto* drive_sizer = new wxBoxSizer(wxHORIZONTAL);
// TRN description of Combo Box with path to USB drive.
wxStaticText* drive_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("Drive")));
m_drive_combo = new ::ComboBox(panel, wxID_ANY, wxString(""), wxDefaultPosition, wxDefaultSize, 0, nullptr, DD_NO_CHECK_ICON);
rescan_drives(preffered_drive);
m_drive_button_id = NewControlId();
// TRN Text of button to rescan connect usb drives in Wifi Config dialog.
wxButton* drive_button = new wxButton(panel, m_drive_button_id, _(L("Rescan")));
drive_sizer->Add(m_drive_combo, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, 10);
drive_sizer->Add(drive_button, 0);
// TRN Text of button to write config file in Wifi Config dialog.
wxButton* ok_button = new wxButton(panel, wxID_OK, _L("Write"));
wxButton* cancel_button = new wxButton(panel, wxID_CANCEL);
auto* grid = new wxFlexGridSizer(2, 15, 15);
grid->AddGrowableCol(1);
grid->Add(ssid_label, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(ssid_sizer, 0, wxEXPAND);
grid->Add(password_label, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(pass_sizer, 0, wxEXPAND);
grid->Add(drive_label, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(drive_sizer, 0, wxEXPAND);
vsizer->Add(explain_label1, 0, wxALIGN_CENTER_VERTICAL);
vsizer->Add(explain_label2, 0, wxALIGN_CENTER_VERTICAL);
vsizer->Add(explain_label3, 0, wxALIGN_CENTER_VERTICAL);
vsizer->Add(explain_label4, 0, wxALIGN_CENTER_VERTICAL);
vsizer->Add(grid, 0, wxEXPAND | wxTOP | wxBOTTOM, 15);
wxBoxSizer* buttons_sizer = new wxBoxSizer(wxHORIZONTAL);
buttons_sizer->Add(ok_button, 1, wxLEFT);
buttons_sizer->AddStretchSpacer();
buttons_sizer->Add(cancel_button, 1, wxRIGHT);
vsizer->Add(buttons_sizer, 0, wxEXPAND);
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
topsizer->Add(panel, 1, wxEXPAND | wxALL, 15);
SetSizerAndFit(topsizer);
ok_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_ok, this);
m_ssid_combo->Bind(wxEVT_TEXT, &WifiConfigDialog::on_combo, this);
drive_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_rescan_drives, this);
ssid_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_rescan_networks, this);
wxGetApp().UpdateDlgDarkUI(this);
}
WifiConfigDialog::~WifiConfigDialog()
{
if (m_wifi_scanner)
delete m_wifi_scanner;
}
void WifiConfigDialog::on_combo(wxCommandEvent& e)
{
fill_password();
}
void WifiConfigDialog::fill_password()
{
assert(m_ssid_combo && m_pass_textctrl && m_wifi_scanner);
if (auto it = m_wifi_scanner->get_map().find(m_ssid_combo->GetValue()); it != m_wifi_scanner->get_map().end())
m_pass_textctrl->SetValue(boost::nowide::widen(it->second));
}
void WifiConfigDialog::on_retrieve_password(wxCommandEvent& e)
{
if (m_ssid_combo->GetValue().empty()) {
return;
}
std::string psk = m_wifi_scanner->get_psk(boost::nowide::narrow(m_ssid_combo->GetValue()));
if (psk.empty()) {
// TRN Alert message when retrieving password for wifi from keychain. Probably will display only on Apple so keychain is MacOS term.
wxString msg = _L("No password in the keychain for given SSID.");
show_info(nullptr, msg);
return;
}
m_pass_textctrl->SetValue(boost::nowide::widen(psk));
}
void WifiConfigDialog::on_rescan_drives(wxCommandEvent& e)
{
rescan_drives({});
}
void WifiConfigDialog::rescan_drives(const wxString& preffered_drive)
{
assert(m_drive_combo && m_removable_manager);
m_drive_combo->Clear();
std::vector<DriveData> ddata = m_removable_manager->get_drive_list();
for (const auto& data : ddata) {
wxString item = boost::nowide::widen(data.path);
m_drive_combo->Append(item);
if (preffered_drive == item)
m_ssid_combo->Select(m_ssid_combo->GetCount() - 1);
}
if (m_drive_combo->GetSelection() == -1 && m_drive_combo->GetCount() > 0) {
m_drive_combo->Select(0);
}
}
void WifiConfigDialog::on_rescan_networks(wxCommandEvent& e)
{
rescan_networks(true);
}
void WifiConfigDialog::rescan_networks(bool select)
{
assert(m_ssid_combo && m_wifi_scanner);
m_wifi_scanner->scan();
std::string current = m_wifi_scanner->get_current_ssid();
const auto& map = m_wifi_scanner->get_map();
m_ssid_combo->Clear();
for (const auto &pair : map) {
m_ssid_combo->Append(pair.first);
// select ssid of current network (if connected)
if (current == pair.first)
m_ssid_combo->Select(m_ssid_combo->GetCount() - 1);
}
if (m_ssid_combo->GetSelection() == -1 && m_ssid_combo->GetCount() > 0)
m_ssid_combo->Select(0);
if (select && m_ssid_combo->GetSelection() != -1)
fill_password();
}
void WifiConfigDialog::on_ok(wxCommandEvent& e)
{
assert(m_ssid_combo && m_pass_textctrl);
if (m_ssid_combo->GetValue().empty()) {
// TRN Alert message when writing WiFi configuration file to usb drive.
wxString msg = _L("SSID field is empty.");
show_info(nullptr, msg);
return;
}
std::string selected_path = boost::nowide::narrow(m_drive_combo->GetValue());
if (selected_path.empty()) {
// TRN Alert message when writing WiFi configuration file to usb drive.
wxString msg = _L("Drive field is empty.");
show_info(nullptr, msg);
return;
}
boost::filesystem::path file_path = boost::filesystem::path(selected_path) / WIFI_CONFIGFILE_NAME;
bool path_on_removable_media = m_removable_manager->set_and_verify_last_save_path(file_path.string());
if (!path_on_removable_media) {
// TRN Alert message when writing WiFi configuration file to usb drive.
wxString msg = _L("Selected path is not on removable media.");
show_info(nullptr, msg);
return;
}
if (boost::filesystem::exists(file_path)) {
// TRN placeholder 1 is path to file
wxString msg_text = GUI::format_wxstr(_L("%1% already exists. Do you want to rewrite it?\n(Other items than Wi-Fi credentials will stay unchanged)"), file_path.string());
WarningDialog dialog(m_parent, msg_text, _L("Warning"), wxYES | wxNO);
if (dialog.ShowModal() == wxID_NO) {
return;
}
}
std::string data;
namespace pt = boost::property_tree;
pt::ptree tree;
// File already exist and we only need to add data to it rather than rewrite it.
if (boost::filesystem::exists(file_path)) {
boost::nowide::ifstream ifs(file_path.string());
try {
pt::read_ini(ifs, tree);
}
catch (const boost::property_tree::ini_parser::ini_parser_error& err) {
throw Slic3r::RuntimeError(format("Failed loading ini file \"%1%\"\nError: \"%2%\" at line %3%", file_path, err.message(), err.line()).c_str());
}
if (auto sub = tree.get_optional<std::string>("wifi"); sub) {
tree.erase("wifi");
}
}
pt::ptree subtree;
subtree.put("ssid", m_ssid_combo->GetValue().utf8_string());
subtree.put("psk", m_pass_textctrl->GetValue().utf8_string());
tree.add_child("wifi", subtree);
data.clear();
// manually write ini string (so there is extra line between sections)
for (const auto& section : tree) {
data += "[" + section.first + "]" + "\n";
for (const auto& entry : section.second) {
data += entry.first + " = " + entry.second.get_value<std::string>() + "\n";
}
data += "\n";
}
m_used_path = boost::nowide::widen(file_path.string());
FILE* file;
file = fopen(file_path.string().c_str(), "w");
if (file == NULL) {
BOOST_LOG_TRIVIAL(error) << "Failed to write to file " << file_path;
// TODO show error
show_error(nullptr, _L("Failed to open file for writing."));
return;
}
fwrite(data.c_str(), 1, data.size(), file);
fclose(file);
out_file_path = file_path.string();
this->EndModal(wxID_OK);
}
void WifiConfigDialog::on_dpi_changed(const wxRect& suggested_rect)
{
SetFont(wxGetApp().normal_font());
const int em = em_unit();
msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL, m_ssid_button_id, m_pass_button_id, m_drive_button_id });
Fit();
Refresh();
}
}}// Slicer::GUI